// dear imgui, v1.54 WIP
// (drawing and font code)

// Contains implementation for
// - Default styles
// - ImDrawList
// - ImDrawData
// - ImFontAtlas
// - ImFont
// - Default font data

#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "imgui.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"

#include <stdio.h> // vsnprintf, sscanf, printf
#if !defined(alloca)
#ifdef _WIN32
#include <malloc.h> // alloca
#if !defined(alloca)
#define alloca _alloca // for clang with MS Codegen
#endif
#elif defined(__GLIBC__) || defined(__sun)
#include <alloca.h> // alloca
#else
#include <stdlib.h> // alloca
#endif
#endif

#ifdef _MSC_VER
#pragma warning(disable : 4505) // unreferenced local function has been removed
                                // (stb stuff)
#pragma warning(                                                               \
    disable : 4996) // 'This function or variable may be unsafe': strcpy,
                    // strdup, sprintf, vsnprintf, sscanf, fopen
#define snprintf _snprintf
#endif

#ifdef __clang__
#pragma clang diagnostic ignored                                               \
    "-Wold-style-cast" // warning : use of old-style cast // yes, they are more
                       // terse.
#pragma clang diagnostic ignored                                               \
    "-Wfloat-equal" // warning : comparing floating point with == or != is
                    // unsafe   // storing and comparing against same constants
                    // ok.
#pragma clang diagnostic ignored                                               \
    "-Wglobal-constructors" // warning : declaration requires a global
                            // destructor           // similar to above, not
                            // sure what the exact difference it.
#pragma clang diagnostic ignored                                               \
    "-Wsign-conversion" // warning : implicit conversion changes signedness //
#if __has_warning("-Wcomma")
#pragma clang diagnostic ignored                                               \
    "-Wcomma" // warning : possible misuse of comma operator here //
#endif
#if __has_warning("-Wreserved-id-macro")
#pragma clang diagnostic ignored                                               \
    "-Wreserved-id-macro" // warning : macro name is a reserved identifier //
#endif
#if __has_warning("-Wdouble-promotion")
#pragma clang diagnostic ignored                                               \
    "-Wdouble-promotion" // warning: implicit conversion from 'float' to
                         // 'double' when passing argument to function
#endif
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored                                                 \
    "-Wunused-function" // warning: 'xxxx' defined but not used
#pragma GCC diagnostic ignored                                                 \
    "-Wdouble-promotion" // warning: implicit conversion from 'float' to
                         // 'double' when passing argument to function
#pragma GCC diagnostic ignored                                                 \
    "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its
                   // value
#pragma GCC diagnostic ignored                                                 \
    "-Wcast-qual" // warning: cast from type 'xxxx' to type 'xxxx' casts away
                  // qualifiers
#endif

//-------------------------------------------------------------------------
// STB libraries implementation
//-------------------------------------------------------------------------

//#define IMGUI_STB_NAMESPACE     ImGuiStb
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION

#ifdef IMGUI_STB_NAMESPACE
namespace IMGUI_STB_NAMESPACE
{
#endif

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(                                                               \
    disable : 4456) // declaration of 'xx' hides previous local declaration
#endif

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored                                                 \
    "-Wtype-limits" // warning: comparison is always true due to limited range
                    // of data type [-Wtype-limits]
#endif

#define STBRP_ASSERT(x) IM_ASSERT(x)
#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
#define STBRP_STATIC
#define STB_RECT_PACK_IMPLEMENTATION
#endif
#include "stb_rect_pack.h"

#define STBTT_malloc(x, u) ((void)(u), ImGui::MemAlloc(x))
#define STBTT_free(x, u) ((void)(u), ImGui::MemFree(x))
#define STBTT_assert(x) IM_ASSERT(x)
#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#else
#define STBTT_DEF extern
#endif
#include "stb_truetype.h"

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

#ifdef __clang__
#pragma clang diagnostic pop
#endif

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#ifdef IMGUI_STB_NAMESPACE
} // namespace ImGuiStb
using namespace IMGUI_STB_NAMESPACE;
#endif

//-----------------------------------------------------------------------------
// Style functions
//-----------------------------------------------------------------------------

void ImGui::StyleColorsClassic(ImGuiStyle* dst)
{
    ImGuiStyle* style  = dst ? dst : &ImGui::GetStyle();
    ImVec4*     colors = style->Colors;

    colors[ImGuiCol_Text]                 = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
    colors[ImGuiCol_TextDisabled]         = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    colors[ImGuiCol_WindowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
    colors[ImGuiCol_ChildBg]              = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_PopupBg]              = ImVec4(0.11f, 0.11f, 0.14f, 0.92f);
    colors[ImGuiCol_Border]               = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);
    colors[ImGuiCol_BorderShadow]         = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_FrameBg]              = ImVec4(0.43f, 0.43f, 0.43f, 0.39f);
    colors[ImGuiCol_FrameBgHovered]       = ImVec4(0.47f, 0.47f, 0.69f, 0.40f);
    colors[ImGuiCol_FrameBgActive]        = ImVec4(0.42f, 0.41f, 0.64f, 0.69f);
    colors[ImGuiCol_TitleBg]              = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
    colors[ImGuiCol_TitleBgActive]        = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
    colors[ImGuiCol_TitleBgCollapsed]     = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
    colors[ImGuiCol_MenuBarBg]            = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
    colors[ImGuiCol_ScrollbarBg]          = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
    colors[ImGuiCol_ScrollbarGrab]        = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
    colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
    colors[ImGuiCol_ScrollbarGrabActive]  = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);
    colors[ImGuiCol_CheckMark]            = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
    colors[ImGuiCol_SliderGrab]           = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
    colors[ImGuiCol_SliderGrabActive]     = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);
    colors[ImGuiCol_Button]               = ImVec4(0.35f, 0.40f, 0.61f, 0.62f);
    colors[ImGuiCol_ButtonHovered]        = ImVec4(0.40f, 0.48f, 0.71f, 0.79f);
    colors[ImGuiCol_ButtonActive]         = ImVec4(0.46f, 0.54f, 0.80f, 1.00f);
    colors[ImGuiCol_Header]               = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
    colors[ImGuiCol_HeaderHovered]        = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
    colors[ImGuiCol_HeaderActive]         = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
    colors[ImGuiCol_Separator]            = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
    colors[ImGuiCol_SeparatorHovered]     = ImVec4(0.60f, 0.60f, 0.70f, 1.00f);
    colors[ImGuiCol_SeparatorActive]      = ImVec4(0.70f, 0.70f, 0.90f, 1.00f);
    colors[ImGuiCol_ResizeGrip]           = ImVec4(1.00f, 1.00f, 1.00f, 0.16f);
    colors[ImGuiCol_ResizeGripHovered]    = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
    colors[ImGuiCol_ResizeGripActive]     = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
    colors[ImGuiCol_CloseButton]          = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
    colors[ImGuiCol_CloseButtonHovered]   = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
    colors[ImGuiCol_CloseButtonActive]    = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
    colors[ImGuiCol_PlotLines]            = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
    colors[ImGuiCol_PlotLinesHovered]     = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
    colors[ImGuiCol_PlotHistogram]        = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
    colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
    colors[ImGuiCol_TextSelectedBg]       = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
    colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
    colors[ImGuiCol_DragDropTarget]       = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
}

void ImGui::StyleColorsDark(ImGuiStyle* dst)
{
    ImGuiStyle* style  = dst ? dst : &ImGui::GetStyle();
    ImVec4*     colors = style->Colors;

    colors[ImGuiCol_Text]                 = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
    colors[ImGuiCol_TextDisabled]         = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
    colors[ImGuiCol_WindowBg]             = ImVec4(0.06f, 0.06f, 0.06f, 0.94f);
    colors[ImGuiCol_ChildBg]              = ImVec4(1.00f, 1.00f, 1.00f, 0.00f);
    colors[ImGuiCol_PopupBg]              = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
    colors[ImGuiCol_Border]               = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
    colors[ImGuiCol_BorderShadow]         = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_FrameBg]              = ImVec4(0.16f, 0.29f, 0.48f, 0.54f);
    colors[ImGuiCol_FrameBgHovered]       = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
    colors[ImGuiCol_FrameBgActive]        = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
    colors[ImGuiCol_TitleBg]              = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
    colors[ImGuiCol_TitleBgActive]        = ImVec4(0.16f, 0.29f, 0.48f, 1.00f);
    colors[ImGuiCol_TitleBgCollapsed]     = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
    colors[ImGuiCol_MenuBarBg]            = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
    colors[ImGuiCol_ScrollbarBg]          = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
    colors[ImGuiCol_ScrollbarGrab]        = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabActive]  = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
    colors[ImGuiCol_CheckMark]            = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_SliderGrab]           = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
    colors[ImGuiCol_SliderGrabActive]     = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_Button]               = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
    colors[ImGuiCol_ButtonHovered]        = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_ButtonActive]         = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
    colors[ImGuiCol_Header]               = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);
    colors[ImGuiCol_HeaderHovered]        = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
    colors[ImGuiCol_HeaderActive]         = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_Separator] =
        colors[ImGuiCol_Border]; // ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
    colors[ImGuiCol_SeparatorHovered]     = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);
    colors[ImGuiCol_SeparatorActive]      = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);
    colors[ImGuiCol_ResizeGrip]           = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
    colors[ImGuiCol_ResizeGripHovered]    = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
    colors[ImGuiCol_ResizeGripActive]     = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
    colors[ImGuiCol_CloseButton]          = ImVec4(0.41f, 0.41f, 0.41f, 0.50f);
    colors[ImGuiCol_CloseButtonHovered]   = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
    colors[ImGuiCol_CloseButtonActive]    = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
    colors[ImGuiCol_PlotLines]            = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
    colors[ImGuiCol_PlotLinesHovered]     = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
    colors[ImGuiCol_PlotHistogram]        = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
    colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
    colors[ImGuiCol_TextSelectedBg]       = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
    colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
    colors[ImGuiCol_DragDropTarget]       = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
}

// Those light colors are better suited with a thicker font than the default one
// + FrameBorder
void ImGui::StyleColorsLight(ImGuiStyle* dst)
{
    ImGuiStyle* style  = dst ? dst : &ImGui::GetStyle();
    ImVec4*     colors = style->Colors;

    colors[ImGuiCol_Text]         = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
    colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    // colors[ImGuiCol_TextHovered]          = ImVec4(1.00f, 1.00f,
    // 1.00f, 1.00f); colors[ImGuiCol_TextActive]           =
    // ImVec4(1.00f, 1.00f, 0.00f, 1.00f);
    colors[ImGuiCol_WindowBg]             = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);
    colors[ImGuiCol_ChildBg]              = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_PopupBg]              = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);
    colors[ImGuiCol_Border]               = ImVec4(0.00f, 0.00f, 0.00f, 0.30f);
    colors[ImGuiCol_BorderShadow]         = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_FrameBg]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
    colors[ImGuiCol_FrameBgHovered]       = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
    colors[ImGuiCol_FrameBgActive]        = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
    colors[ImGuiCol_TitleBg]              = ImVec4(0.96f, 0.96f, 0.96f, 1.00f);
    colors[ImGuiCol_TitleBgActive]        = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);
    colors[ImGuiCol_TitleBgCollapsed]     = ImVec4(1.00f, 1.00f, 1.00f, 0.51f);
    colors[ImGuiCol_MenuBarBg]            = ImVec4(0.86f, 0.86f, 0.86f, 1.00f);
    colors[ImGuiCol_ScrollbarBg]          = ImVec4(0.98f, 0.98f, 0.98f, 0.53f);
    colors[ImGuiCol_ScrollbarGrab]        = ImVec4(0.69f, 0.69f, 0.69f, 0.80f);
    colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f);
    colors[ImGuiCol_ScrollbarGrabActive]  = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);
    colors[ImGuiCol_CheckMark]            = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_SliderGrab]           = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);
    colors[ImGuiCol_SliderGrabActive]     = ImVec4(0.46f, 0.54f, 0.80f, 0.60f);
    colors[ImGuiCol_Button]               = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
    colors[ImGuiCol_ButtonHovered]        = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_ButtonActive]         = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
    colors[ImGuiCol_Header]               = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);
    colors[ImGuiCol_HeaderHovered]        = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
    colors[ImGuiCol_HeaderActive]         = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_Separator]            = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
    colors[ImGuiCol_SeparatorHovered]     = ImVec4(0.14f, 0.44f, 0.80f, 0.78f);
    colors[ImGuiCol_SeparatorActive]      = ImVec4(0.14f, 0.44f, 0.80f, 1.00f);
    colors[ImGuiCol_ResizeGrip]           = ImVec4(0.80f, 0.80f, 0.80f, 0.56f);
    colors[ImGuiCol_ResizeGripHovered]    = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
    colors[ImGuiCol_ResizeGripActive]     = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
    colors[ImGuiCol_CloseButton]          = ImVec4(0.59f, 0.59f, 0.59f, 0.50f);
    colors[ImGuiCol_CloseButtonHovered]   = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
    colors[ImGuiCol_CloseButtonActive]    = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
    colors[ImGuiCol_PlotLines]            = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
    colors[ImGuiCol_PlotLinesHovered]     = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
    colors[ImGuiCol_PlotHistogram]        = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
    colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f);
    colors[ImGuiCol_TextSelectedBg]       = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
    colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
    colors[ImGuiCol_DragDropTarget]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
}

//-----------------------------------------------------------------------------
// ImDrawListData
//-----------------------------------------------------------------------------

ImDrawListSharedData::ImDrawListSharedData()
{
    Font                 = NULL;
    FontSize             = 0.0f;
    CurveTessellationTol = 0.0f;
    ClipRectFullscreen   = ImVec4(-8192.0f, -8192.0f, +8192.0f, +8192.0f);

    // Const data
    for (int i = 0; i < IM_ARRAYSIZE(CircleVtx12); i++)
    {
        const float a =
            ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(CircleVtx12);
        CircleVtx12[i] = ImVec2(cosf(a), sinf(a));
    }
}

//-----------------------------------------------------------------------------
// ImDrawList
//-----------------------------------------------------------------------------

void ImDrawList::Clear()
{
    CmdBuffer.resize(0);
    IdxBuffer.resize(0);
    VtxBuffer.resize(0);
    Flags = ImDrawListFlags_AntiAliasedLines | ImDrawListFlags_AntiAliasedFill;
    _VtxCurrentIdx = 0;
    _VtxWritePtr   = NULL;
    _IdxWritePtr   = NULL;
    _ClipRectStack.resize(0);
    _TextureIdStack.resize(0);
    _Path.resize(0);
    _ChannelsCurrent = 0;
    _ChannelsCount   = 1;
    // NB: Do not clear channels so our allocations are re-used after the first
    // frame.
}

void ImDrawList::ClearFreeMemory()
{
    CmdBuffer.clear();
    IdxBuffer.clear();
    VtxBuffer.clear();
    _VtxCurrentIdx = 0;
    _VtxWritePtr   = NULL;
    _IdxWritePtr   = NULL;
    _ClipRectStack.clear();
    _TextureIdStack.clear();
    _Path.clear();
    _ChannelsCurrent = 0;
    _ChannelsCount   = 1;
    for (int i = 0; i < _Channels.Size; i++)
    {
        if (i == 0)
            memset(
                (void*)&_Channels[0], 0,
                sizeof(
                    _Channels[0])); // channel 0 is a copy of
                                    // CmdBuffer/IdxBuffer, don't destruct again
        _Channels[i].CmdBuffer.clear();
        _Channels[i].IdxBuffer.clear();
    }
    _Channels.clear();
}

// Using macros because C++ is a terrible language, we want guaranteed inline,
// no code in header, and no overhead in Debug builds
#define GetCurrentClipRect()                                                   \
    (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size - 1]        \
                         : _Data->ClipRectFullscreen)
#define GetCurrentTextureId()                                                  \
    (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size - 1]     \
                          : NULL)

void ImDrawList::AddDrawCmd()
{
    ImDrawCmd draw_cmd;
    draw_cmd.ClipRect  = GetCurrentClipRect();
    draw_cmd.TextureId = GetCurrentTextureId();

    IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z &&
              draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);
    CmdBuffer.push_back(draw_cmd);
}

void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
{
    ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL;
    if (!current_cmd || current_cmd->ElemCount != 0 ||
        current_cmd->UserCallback != NULL)
    {
        AddDrawCmd();
        current_cmd = &CmdBuffer.back();
    }
    current_cmd->UserCallback     = callback;
    current_cmd->UserCallbackData = callback_data;

    AddDrawCmd(); // Force a new command after us (see comment below)
}

// Our scheme may appears a bit unusual, basically we want the most-common calls
// AddLine AddRect etc. to not have to perform any check so we always have a
// command ready in the stack. The cost of figuring out if a new command has to
// be added or if we can merge is paid in those Update** functions only.
void ImDrawList::UpdateClipRect()
{
    // If current command is used with different settings we need to add a new
    // command
    const ImVec4 curr_clip_rect = GetCurrentClipRect();
    ImDrawCmd*   curr_cmd =
        CmdBuffer.Size > 0 ? &CmdBuffer.Data[CmdBuffer.Size - 1] : NULL;
    if (!curr_cmd ||
        (curr_cmd->ElemCount != 0 &&
         memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) ||
        curr_cmd->UserCallback != NULL)
    {
        AddDrawCmd();
        return;
    }

    // Try to merge with previous command if it matches, else use current
    // command
    ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
    if (curr_cmd->ElemCount == 0 && prev_cmd &&
        memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 &&
        prev_cmd->TextureId == GetCurrentTextureId() &&
        prev_cmd->UserCallback == NULL)
        CmdBuffer.pop_back();
    else
        curr_cmd->ClipRect = curr_clip_rect;
}

void ImDrawList::UpdateTextureID()
{
    // If current command is used with different settings we need to add a new
    // command
    const ImTextureID curr_texture_id = GetCurrentTextureId();
    ImDrawCmd*        curr_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL;
    if (!curr_cmd ||
        (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) ||
        curr_cmd->UserCallback != NULL)
    {
        AddDrawCmd();
        return;
    }

    // Try to merge with previous command if it matches, else use current
    // command
    ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
    if (curr_cmd->ElemCount == 0 && prev_cmd &&
        prev_cmd->TextureId == curr_texture_id &&
        memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) ==
            0 &&
        prev_cmd->UserCallback == NULL)
        CmdBuffer.pop_back();
    else
        curr_cmd->TextureId = curr_texture_id;
}

#undef GetCurrentClipRect
#undef GetCurrentTextureId

// Render-level scissoring. This is passed down to your render function but not
// used for CPU-side coarse clipping. Prefer using higher-level
// ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max,
                              bool intersect_with_current_clip_rect)
{
    ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y);
    if (intersect_with_current_clip_rect && _ClipRectStack.Size)
    {
        ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size - 1];
        if (cr.x < current.x)
            cr.x = current.x;
        if (cr.y < current.y)
            cr.y = current.y;
        if (cr.z > current.z)
            cr.z = current.z;
        if (cr.w > current.w)
            cr.w = current.w;
    }
    cr.z = ImMax(cr.x, cr.z);
    cr.w = ImMax(cr.y, cr.w);

    _ClipRectStack.push_back(cr);
    UpdateClipRect();
}

void ImDrawList::PushClipRectFullScreen()
{
    PushClipRect(
        ImVec2(_Data->ClipRectFullscreen.x, _Data->ClipRectFullscreen.y),
        ImVec2(_Data->ClipRectFullscreen.z, _Data->ClipRectFullscreen.w));
}

void ImDrawList::PopClipRect()
{
    IM_ASSERT(_ClipRectStack.Size > 0);
    _ClipRectStack.pop_back();
    UpdateClipRect();
}

void ImDrawList::PushTextureID(const ImTextureID& texture_id)
{
    _TextureIdStack.push_back(texture_id);
    UpdateTextureID();
}

void ImDrawList::PopTextureID()
{
    IM_ASSERT(_TextureIdStack.Size > 0);
    _TextureIdStack.pop_back();
    UpdateTextureID();
}

void ImDrawList::ChannelsSplit(int channels_count)
{
    IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1);
    int old_channels_count = _Channels.Size;
    if (old_channels_count < channels_count)
        _Channels.resize(channels_count);
    _ChannelsCount = channels_count;

    // _Channels[] (24/32 bytes each) hold storage that we'll swap with
    // this->_CmdBuffer/_IdxBuffer The content of _Channels[0] at this point
    // doesn't matter. We clear it to make state tidy in a debugger but we don't
    // strictly need to. When we switch to the next channel, we'll copy
    // _CmdBuffer/_IdxBuffer into _Channels[0] and then _Channels[1] into
    // _CmdBuffer/_IdxBuffer
    memset((void*)&_Channels[0], 0, sizeof(ImDrawChannel));
    for (int i = 1; i < channels_count; i++)
    {
        if (i >= old_channels_count)
        {
            IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel();
        }
        else
        {
            _Channels[i].CmdBuffer.resize(0);
            _Channels[i].IdxBuffer.resize(0);
        }
        if (_Channels[i].CmdBuffer.Size == 0)
        {
            ImDrawCmd draw_cmd;
            draw_cmd.ClipRect  = _ClipRectStack.back();
            draw_cmd.TextureId = _TextureIdStack.back();
            _Channels[i].CmdBuffer.push_back(draw_cmd);
        }
    }
}

void ImDrawList::ChannelsMerge()
{
    // Note that we never use or rely on channels.Size because it is merely a
    // buffer that we never shrink back to 0 to keep all sub-buffers ready for
    // use.
    if (_ChannelsCount <= 1)
        return;

    ChannelsSetCurrent(0);
    if (CmdBuffer.Size && CmdBuffer.back().ElemCount == 0)
        CmdBuffer.pop_back();

    int new_cmd_buffer_count = 0, new_idx_buffer_count = 0;
    for (int i = 1; i < _ChannelsCount; i++)
    {
        ImDrawChannel& ch = _Channels[i];
        if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0)
            ch.CmdBuffer.pop_back();
        new_cmd_buffer_count += ch.CmdBuffer.Size;
        new_idx_buffer_count += ch.IdxBuffer.Size;
    }
    CmdBuffer.resize(CmdBuffer.Size + new_cmd_buffer_count);
    IdxBuffer.resize(IdxBuffer.Size + new_idx_buffer_count);

    ImDrawCmd* cmd_write =
        CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count;
    _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count;
    for (int i = 1; i < _ChannelsCount; i++)
    {
        ImDrawChannel& ch = _Channels[i];
        if (int sz = ch.CmdBuffer.Size)
        {
            memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd));
            cmd_write += sz;
        }
        if (int sz = ch.IdxBuffer.Size)
        {
            memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx));
            _IdxWritePtr += sz;
        }
    }
    UpdateClipRect(); // We call this instead of AddDrawCmd(), so that empty
                      // channels won't produce an extra draw call.
    _ChannelsCount = 1;
}

void ImDrawList::ChannelsSetCurrent(int idx)
{
    IM_ASSERT(idx < _ChannelsCount);
    if (_ChannelsCurrent == idx)
        return;
    memcpy((void*)&_Channels.Data[_ChannelsCurrent].CmdBuffer, &CmdBuffer,
           sizeof(CmdBuffer)); // copy 12 bytes, four times
    memcpy((void*)&_Channels.Data[_ChannelsCurrent].IdxBuffer, &IdxBuffer,
           sizeof(IdxBuffer));
    _ChannelsCurrent = idx;
    memcpy((void*)&CmdBuffer, &_Channels.Data[_ChannelsCurrent].CmdBuffer,
           sizeof(CmdBuffer));
    memcpy((void*)&IdxBuffer, &_Channels.Data[_ChannelsCurrent].IdxBuffer,
           sizeof(IdxBuffer));
    _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size;
}

// NB: this can be called with negative count for removing primitives (as long
// as the result does not underflow)
void ImDrawList::PrimReserve(int idx_count, int vtx_count)
{
    ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size - 1];
    draw_cmd.ElemCount += idx_count;

    int vtx_buffer_old_size = VtxBuffer.Size;
    VtxBuffer.resize(vtx_buffer_old_size + vtx_count);
    _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size;

    int idx_buffer_old_size = IdxBuffer.Size;
    IdxBuffer.resize(idx_buffer_old_size + idx_count);
    _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;
}

// Fully unrolled with inline call to keep our debug builds decently fast.
void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col)
{
    ImVec2    b(c.x, a.y), d(a.x, c.y), uv(_Data->TexUvWhitePixel);
    ImDrawIdx idx       = (ImDrawIdx)_VtxCurrentIdx;
    _IdxWritePtr[0]     = idx;
    _IdxWritePtr[1]     = (ImDrawIdx)(idx + 1);
    _IdxWritePtr[2]     = (ImDrawIdx)(idx + 2);
    _IdxWritePtr[3]     = idx;
    _IdxWritePtr[4]     = (ImDrawIdx)(idx + 2);
    _IdxWritePtr[5]     = (ImDrawIdx)(idx + 3);
    _VtxWritePtr[0].pos = a;
    _VtxWritePtr[0].uv  = uv;
    _VtxWritePtr[0].col = col;
    _VtxWritePtr[1].pos = b;
    _VtxWritePtr[1].uv  = uv;
    _VtxWritePtr[1].col = col;
    _VtxWritePtr[2].pos = c;
    _VtxWritePtr[2].uv  = uv;
    _VtxWritePtr[2].col = col;
    _VtxWritePtr[3].pos = d;
    _VtxWritePtr[3].uv  = uv;
    _VtxWritePtr[3].col = col;
    _VtxWritePtr += 4;
    _VtxCurrentIdx += 4;
    _IdxWritePtr += 6;
}

void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c,
                            const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col)
{
    ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y);
    ImDrawIdx idx       = (ImDrawIdx)_VtxCurrentIdx;
    _IdxWritePtr[0]     = idx;
    _IdxWritePtr[1]     = (ImDrawIdx)(idx + 1);
    _IdxWritePtr[2]     = (ImDrawIdx)(idx + 2);
    _IdxWritePtr[3]     = idx;
    _IdxWritePtr[4]     = (ImDrawIdx)(idx + 2);
    _IdxWritePtr[5]     = (ImDrawIdx)(idx + 3);
    _VtxWritePtr[0].pos = a;
    _VtxWritePtr[0].uv  = uv_a;
    _VtxWritePtr[0].col = col;
    _VtxWritePtr[1].pos = b;
    _VtxWritePtr[1].uv  = uv_b;
    _VtxWritePtr[1].col = col;
    _VtxWritePtr[2].pos = c;
    _VtxWritePtr[2].uv  = uv_c;
    _VtxWritePtr[2].col = col;
    _VtxWritePtr[3].pos = d;
    _VtxWritePtr[3].uv  = uv_d;
    _VtxWritePtr[3].col = col;
    _VtxWritePtr += 4;
    _VtxCurrentIdx += 4;
    _IdxWritePtr += 6;
}

void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c,
                            const ImVec2& d, const ImVec2& uv_a,
                            const ImVec2& uv_b, const ImVec2& uv_c,
                            const ImVec2& uv_d, ImU32 col)
{
    ImDrawIdx idx       = (ImDrawIdx)_VtxCurrentIdx;
    _IdxWritePtr[0]     = idx;
    _IdxWritePtr[1]     = (ImDrawIdx)(idx + 1);
    _IdxWritePtr[2]     = (ImDrawIdx)(idx + 2);
    _IdxWritePtr[3]     = idx;
    _IdxWritePtr[4]     = (ImDrawIdx)(idx + 2);
    _IdxWritePtr[5]     = (ImDrawIdx)(idx + 3);
    _VtxWritePtr[0].pos = a;
    _VtxWritePtr[0].uv  = uv_a;
    _VtxWritePtr[0].col = col;
    _VtxWritePtr[1].pos = b;
    _VtxWritePtr[1].uv  = uv_b;
    _VtxWritePtr[1].col = col;
    _VtxWritePtr[2].pos = c;
    _VtxWritePtr[2].uv  = uv_c;
    _VtxWritePtr[2].col = col;
    _VtxWritePtr[3].pos = d;
    _VtxWritePtr[3].uv  = uv_d;
    _VtxWritePtr[3].col = col;
    _VtxWritePtr += 4;
    _VtxCurrentIdx += 4;
    _IdxWritePtr += 6;
}

// TODO: Thickness anti-aliased lines cap are missing their AA fringe.
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count,
                             ImU32 col, bool closed, float thickness)
{
    if (points_count < 2)
        return;

    const ImVec2 uv = _Data->TexUvWhitePixel;

    int count = points_count;
    if (!closed)
        count = points_count - 1;

    const bool thick_line = thickness > 1.0f;
    if (Flags & ImDrawListFlags_AntiAliasedLines)
    {
        // Anti-aliased stroke
        const float AA_SIZE   = 1.0f;
        const ImU32 col_trans = col & ~IM_COL32_A_MASK;

        const int idx_count = thick_line ? count * 18 : count * 12;
        const int vtx_count = thick_line ? points_count * 4 : points_count * 3;
        PrimReserve(idx_count, vtx_count);

        // Temporary buffer
        ImVec2* temp_normals = (ImVec2*)alloca(
            points_count * (thick_line ? 5 : 3) * sizeof(ImVec2));
        ImVec2* temp_points = temp_normals + points_count;

        for (int i1 = 0; i1 < count; i1++)
        {
            const int i2   = (i1 + 1) == points_count ? 0 : i1 + 1;
            ImVec2    diff = points[i2] - points[i1];
            diff *= ImInvLength(diff, 1.0f);
            temp_normals[i1].x = diff.y;
            temp_normals[i1].y = -diff.x;
        }
        if (!closed)
            temp_normals[points_count - 1] = temp_normals[points_count - 2];

        if (!thick_line)
        {
            if (!closed)
            {
                temp_points[0] = points[0] + temp_normals[0] * AA_SIZE;
                temp_points[1] = points[0] - temp_normals[0] * AA_SIZE;
                temp_points[(points_count - 1) * 2 + 0] =
                    points[points_count - 1] +
                    temp_normals[points_count - 1] * AA_SIZE;
                temp_points[(points_count - 1) * 2 + 1] =
                    points[points_count - 1] -
                    temp_normals[points_count - 1] * AA_SIZE;
            }

            // FIXME-OPT: Merge the different loops, possibly remove the
            // temporary buffer.
            unsigned int idx1 = _VtxCurrentIdx;
            for (int i1 = 0; i1 < count; i1++)
            {
                const int    i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
                unsigned int idx2 =
                    (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 3;

                // Average normals
                ImVec2 dm   = (temp_normals[i1] + temp_normals[i2]) * 0.5f;
                float  dmr2 = dm.x * dm.x + dm.y * dm.y;
                if (dmr2 > 0.000001f)
                {
                    float scale = 1.0f / dmr2;
                    if (scale > 100.0f)
                        scale = 100.0f;
                    dm *= scale;
                }
                dm *= AA_SIZE;
                temp_points[i2 * 2 + 0] = points[i2] + dm;
                temp_points[i2 * 2 + 1] = points[i2] - dm;

                // Add indexes
                _IdxWritePtr[0]  = (ImDrawIdx)(idx2 + 0);
                _IdxWritePtr[1]  = (ImDrawIdx)(idx1 + 0);
                _IdxWritePtr[2]  = (ImDrawIdx)(idx1 + 2);
                _IdxWritePtr[3]  = (ImDrawIdx)(idx1 + 2);
                _IdxWritePtr[4]  = (ImDrawIdx)(idx2 + 2);
                _IdxWritePtr[5]  = (ImDrawIdx)(idx2 + 0);
                _IdxWritePtr[6]  = (ImDrawIdx)(idx2 + 1);
                _IdxWritePtr[7]  = (ImDrawIdx)(idx1 + 1);
                _IdxWritePtr[8]  = (ImDrawIdx)(idx1 + 0);
                _IdxWritePtr[9]  = (ImDrawIdx)(idx1 + 0);
                _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0);
                _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1);
                _IdxWritePtr += 12;

                idx1 = idx2;
            }

            // Add vertexes
            for (int i = 0; i < points_count; i++)
            {
                _VtxWritePtr[0].pos = points[i];
                _VtxWritePtr[0].uv  = uv;
                _VtxWritePtr[0].col = col;
                _VtxWritePtr[1].pos = temp_points[i * 2 + 0];
                _VtxWritePtr[1].uv  = uv;
                _VtxWritePtr[1].col = col_trans;
                _VtxWritePtr[2].pos = temp_points[i * 2 + 1];
                _VtxWritePtr[2].uv  = uv;
                _VtxWritePtr[2].col = col_trans;
                _VtxWritePtr += 3;
            }
        }
        else
        {
            const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;
            if (!closed)
            {
                temp_points[0] =
                    points[0] +
                    temp_normals[0] * (half_inner_thickness + AA_SIZE);
                temp_points[1] =
                    points[0] + temp_normals[0] * (half_inner_thickness);
                temp_points[2] =
                    points[0] - temp_normals[0] * (half_inner_thickness);
                temp_points[3] =
                    points[0] -
                    temp_normals[0] * (half_inner_thickness + AA_SIZE);
                temp_points[(points_count - 1) * 4 + 0] =
                    points[points_count - 1] +
                    temp_normals[points_count - 1] *
                        (half_inner_thickness + AA_SIZE);
                temp_points[(points_count - 1) * 4 + 1] =
                    points[points_count - 1] +
                    temp_normals[points_count - 1] * (half_inner_thickness);
                temp_points[(points_count - 1) * 4 + 2] =
                    points[points_count - 1] -
                    temp_normals[points_count - 1] * (half_inner_thickness);
                temp_points[(points_count - 1) * 4 + 3] =
                    points[points_count - 1] -
                    temp_normals[points_count - 1] *
                        (half_inner_thickness + AA_SIZE);
            }

            // FIXME-OPT: Merge the different loops, possibly remove the
            // temporary buffer.
            unsigned int idx1 = _VtxCurrentIdx;
            for (int i1 = 0; i1 < count; i1++)
            {
                const int    i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
                unsigned int idx2 =
                    (i1 + 1) == points_count ? _VtxCurrentIdx : idx1 + 4;

                // Average normals
                ImVec2 dm   = (temp_normals[i1] + temp_normals[i2]) * 0.5f;
                float  dmr2 = dm.x * dm.x + dm.y * dm.y;
                if (dmr2 > 0.000001f)
                {
                    float scale = 1.0f / dmr2;
                    if (scale > 100.0f)
                        scale = 100.0f;
                    dm *= scale;
                }
                ImVec2 dm_out           = dm * (half_inner_thickness + AA_SIZE);
                ImVec2 dm_in            = dm * half_inner_thickness;
                temp_points[i2 * 4 + 0] = points[i2] + dm_out;
                temp_points[i2 * 4 + 1] = points[i2] + dm_in;
                temp_points[i2 * 4 + 2] = points[i2] - dm_in;
                temp_points[i2 * 4 + 3] = points[i2] - dm_out;

                // Add indexes
                _IdxWritePtr[0]  = (ImDrawIdx)(idx2 + 1);
                _IdxWritePtr[1]  = (ImDrawIdx)(idx1 + 1);
                _IdxWritePtr[2]  = (ImDrawIdx)(idx1 + 2);
                _IdxWritePtr[3]  = (ImDrawIdx)(idx1 + 2);
                _IdxWritePtr[4]  = (ImDrawIdx)(idx2 + 2);
                _IdxWritePtr[5]  = (ImDrawIdx)(idx2 + 1);
                _IdxWritePtr[6]  = (ImDrawIdx)(idx2 + 1);
                _IdxWritePtr[7]  = (ImDrawIdx)(idx1 + 1);
                _IdxWritePtr[8]  = (ImDrawIdx)(idx1 + 0);
                _IdxWritePtr[9]  = (ImDrawIdx)(idx1 + 0);
                _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0);
                _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1);
                _IdxWritePtr[12] = (ImDrawIdx)(idx2 + 2);
                _IdxWritePtr[13] = (ImDrawIdx)(idx1 + 2);
                _IdxWritePtr[14] = (ImDrawIdx)(idx1 + 3);
                _IdxWritePtr[15] = (ImDrawIdx)(idx1 + 3);
                _IdxWritePtr[16] = (ImDrawIdx)(idx2 + 3);
                _IdxWritePtr[17] = (ImDrawIdx)(idx2 + 2);
                _IdxWritePtr += 18;

                idx1 = idx2;
            }

            // Add vertexes
            for (int i = 0; i < points_count; i++)
            {
                _VtxWritePtr[0].pos = temp_points[i * 4 + 0];
                _VtxWritePtr[0].uv  = uv;
                _VtxWritePtr[0].col = col_trans;
                _VtxWritePtr[1].pos = temp_points[i * 4 + 1];
                _VtxWritePtr[1].uv  = uv;
                _VtxWritePtr[1].col = col;
                _VtxWritePtr[2].pos = temp_points[i * 4 + 2];
                _VtxWritePtr[2].uv  = uv;
                _VtxWritePtr[2].col = col;
                _VtxWritePtr[3].pos = temp_points[i * 4 + 3];
                _VtxWritePtr[3].uv  = uv;
                _VtxWritePtr[3].col = col_trans;
                _VtxWritePtr += 4;
            }
        }
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
    }
    else
    {
        // Non Anti-aliased Stroke
        const int idx_count = count * 6;
        const int vtx_count = count * 4; // FIXME-OPT: Not sharing edges
        PrimReserve(idx_count, vtx_count);

        for (int i1 = 0; i1 < count; i1++)
        {
            const int     i2   = (i1 + 1) == points_count ? 0 : i1 + 1;
            const ImVec2& p1   = points[i1];
            const ImVec2& p2   = points[i2];
            ImVec2        diff = p2 - p1;
            diff *= ImInvLength(diff, 1.0f);

            const float dx        = diff.x * (thickness * 0.5f);
            const float dy        = diff.y * (thickness * 0.5f);
            _VtxWritePtr[0].pos.x = p1.x + dy;
            _VtxWritePtr[0].pos.y = p1.y - dx;
            _VtxWritePtr[0].uv    = uv;
            _VtxWritePtr[0].col   = col;
            _VtxWritePtr[1].pos.x = p2.x + dy;
            _VtxWritePtr[1].pos.y = p2.y - dx;
            _VtxWritePtr[1].uv    = uv;
            _VtxWritePtr[1].col   = col;
            _VtxWritePtr[2].pos.x = p2.x - dy;
            _VtxWritePtr[2].pos.y = p2.y + dx;
            _VtxWritePtr[2].uv    = uv;
            _VtxWritePtr[2].col   = col;
            _VtxWritePtr[3].pos.x = p1.x - dy;
            _VtxWritePtr[3].pos.y = p1.y + dx;
            _VtxWritePtr[3].uv    = uv;
            _VtxWritePtr[3].col   = col;
            _VtxWritePtr += 4;

            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx);
            _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + 1);
            _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + 2);
            _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx);
            _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx + 2);
            _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + 3);
            _IdxWritePtr += 6;
            _VtxCurrentIdx += 4;
        }
    }
}

void ImDrawList::AddConvexPolyFilled(const ImVec2* points,
                                     const int points_count, ImU32 col)
{
    const ImVec2 uv = _Data->TexUvWhitePixel;

    if (Flags & ImDrawListFlags_AntiAliasedFill)
    {
        // Anti-aliased Fill
        const float AA_SIZE   = 1.0f;
        const ImU32 col_trans = col & ~IM_COL32_A_MASK;
        const int   idx_count = (points_count - 2) * 3 + points_count * 6;
        const int   vtx_count = (points_count * 2);
        PrimReserve(idx_count, vtx_count);

        // Add indexes for fill
        unsigned int vtx_inner_idx = _VtxCurrentIdx;
        unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
        for (int i = 2; i < points_count; i++)
        {
            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx);
            _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + ((i - 1) << 1));
            _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (i << 1));
            _IdxWritePtr += 3;
        }

        // Compute normals
        ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2));
        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
        {
            const ImVec2& p0   = points[i0];
            const ImVec2& p1   = points[i1];
            ImVec2        diff = p1 - p0;
            diff *= ImInvLength(diff, 1.0f);
            temp_normals[i0].x = diff.y;
            temp_normals[i0].y = -diff.x;
        }

        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
        {
            // Average normals
            const ImVec2& n0   = temp_normals[i0];
            const ImVec2& n1   = temp_normals[i1];
            ImVec2        dm   = (n0 + n1) * 0.5f;
            float         dmr2 = dm.x * dm.x + dm.y * dm.y;
            if (dmr2 > 0.000001f)
            {
                float scale = 1.0f / dmr2;
                if (scale > 100.0f)
                    scale = 100.0f;
                dm *= scale;
            }
            dm *= AA_SIZE * 0.5f;

            // Add vertices
            _VtxWritePtr[0].pos = (points[i1] - dm);
            _VtxWritePtr[0].uv  = uv;
            _VtxWritePtr[0].col = col; // Inner
            _VtxWritePtr[1].pos = (points[i1] + dm);
            _VtxWritePtr[1].uv  = uv;
            _VtxWritePtr[1].col = col_trans; // Outer
            _VtxWritePtr += 2;

            // Add indexes for fringes
            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
            _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1));
            _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
            _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
            _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1));
            _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
            _IdxWritePtr += 6;
        }
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
    }
    else
    {
        // Non Anti-aliased Fill
        const int idx_count = (points_count - 2) * 3;
        const int vtx_count = points_count;
        PrimReserve(idx_count, vtx_count);
        for (int i = 0; i < vtx_count; i++)
        {
            _VtxWritePtr[0].pos = points[i];
            _VtxWritePtr[0].uv  = uv;
            _VtxWritePtr[0].col = col;
            _VtxWritePtr++;
        }
        for (int i = 2; i < points_count; i++)
        {
            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx);
            _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i - 1);
            _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + i);
            _IdxWritePtr += 3;
        }
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
    }
}

void ImDrawList::PathArcToFast(const ImVec2& centre, float radius,
                               int a_min_of_12, int a_max_of_12)
{
    if (radius == 0.0f || a_min_of_12 > a_max_of_12)
    {
        _Path.push_back(centre);
        return;
    }
    _Path.reserve(_Path.Size + (a_max_of_12 - a_min_of_12 + 1));
    for (int a = a_min_of_12; a <= a_max_of_12; a++)
    {
        const ImVec2& c =
            _Data->CircleVtx12[a % IM_ARRAYSIZE(_Data->CircleVtx12)];
        _Path.push_back(
            ImVec2(centre.x + c.x * radius, centre.y + c.y * radius));
    }
}

void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float a_min,
                           float a_max, int num_segments)
{
    if (radius == 0.0f)
    {
        _Path.push_back(centre);
        return;
    }
    _Path.reserve(_Path.Size + (num_segments + 1));
    for (int i = 0; i <= num_segments; i++)
    {
        const float a =
            a_min + ((float)i / (float)num_segments) * (a_max - a_min);
        _Path.push_back(
            ImVec2(centre.x + cosf(a) * radius, centre.y + sinf(a) * radius));
    }
}

static void PathBezierToCasteljau(ImVector<ImVec2>* path, float x1, float y1,
                                  float x2, float y2, float x3, float y3,
                                  float x4, float y4, float tess_tol, int level)
{
    float dx = x4 - x1;
    float dy = y4 - y1;
    float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
    float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
    d2       = (d2 >= 0) ? d2 : -d2;
    d3       = (d3 >= 0) ? d3 : -d3;
    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
    {
        path->push_back(ImVec2(x4, y4));
    }
    else if (level < 10)
    {
        float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f;
        float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f;
        float x34 = (x3 + x4) * 0.5f, y34 = (y3 + y4) * 0.5f;
        float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;
        float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f;
        float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f;

        PathBezierToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234,
                              tess_tol, level + 1);
        PathBezierToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4,
                              tess_tol, level + 1);
    }
}

void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3,
                                   const ImVec2& p4, int num_segments)
{
    ImVec2 p1 = _Path.back();
    if (num_segments == 0)
    {
        // Auto-tessellated
        PathBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x,
                              p4.y, _Data->CurveTessellationTol, 0);
    }
    else
    {
        float t_step = 1.0f / (float)num_segments;
        for (int i_step = 1; i_step <= num_segments; i_step++)
        {
            float t  = t_step * i_step;
            float u  = 1.0f - t;
            float w1 = u * u * u;
            float w2 = 3 * u * u * t;
            float w3 = 3 * u * t * t;
            float w4 = t * t * t;
            _Path.push_back(
                ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x,
                       w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y));
        }
    }
}

void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding,
                          int rounding_corners)
{
    rounding =
        ImMin(rounding,
              fabsf(b.x - a.x) *
                      (((rounding_corners & ImDrawCornerFlags_Top) ==
                        ImDrawCornerFlags_Top) ||
                               ((rounding_corners & ImDrawCornerFlags_Bot) ==
                                ImDrawCornerFlags_Bot)
                           ? 0.5f
                           : 1.0f) -
                  1.0f);
    rounding =
        ImMin(rounding,
              fabsf(b.y - a.y) *
                      (((rounding_corners & ImDrawCornerFlags_Left) ==
                        ImDrawCornerFlags_Left) ||
                               ((rounding_corners & ImDrawCornerFlags_Right) ==
                                ImDrawCornerFlags_Right)
                           ? 0.5f
                           : 1.0f) -
                  1.0f);

    if (rounding <= 0.0f || rounding_corners == 0)
    {
        PathLineTo(a);
        PathLineTo(ImVec2(b.x, a.y));
        PathLineTo(b);
        PathLineTo(ImVec2(a.x, b.y));
    }
    else
    {
        const float rounding_tl =
            (rounding_corners & ImDrawCornerFlags_TopLeft) ? rounding : 0.0f;
        const float rounding_tr =
            (rounding_corners & ImDrawCornerFlags_TopRight) ? rounding : 0.0f;
        const float rounding_br =
            (rounding_corners & ImDrawCornerFlags_BotRight) ? rounding : 0.0f;
        const float rounding_bl =
            (rounding_corners & ImDrawCornerFlags_BotLeft) ? rounding : 0.0f;
        PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl,
                      6, 9);
        PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr,
                      9, 12);
        PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br,
                      0, 3);
        PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl,
                      3, 6);
    }
}

void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col,
                         float thickness)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;
    PathLineTo(a + ImVec2(0.5f, 0.5f));
    PathLineTo(b + ImVec2(0.5f, 0.5f));
    PathStroke(col, false, thickness);
}

// a: upper-left, b: lower-right. we don't render 1 px sized rectangles
// properly.
void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col,
                         float rounding, int rounding_corners_flags,
                         float thickness)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;
    PathRect(a + ImVec2(0.5f, 0.5f), b - ImVec2(0.5f, 0.5f), rounding,
             rounding_corners_flags);
    PathStroke(col, true, thickness);
}

void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col,
                               float rounding, int rounding_corners_flags)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;
    if (rounding > 0.0f)
    {
        PathRect(a, b, rounding, rounding_corners_flags);
        PathFillConvex(col);
    }
    else
    {
        PrimReserve(6, 4);
        PrimRect(a, b, col);
    }
}

void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& c,
                                         ImU32 col_upr_left,
                                         ImU32 col_upr_right,
                                         ImU32 col_bot_right,
                                         ImU32 col_bot_left)
{
    if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) &
         IM_COL32_A_MASK) == 0)
        return;

    const ImVec2 uv = _Data->TexUvWhitePixel;
    PrimReserve(6, 4);
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx));
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1));
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2));
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx));
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2));
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 3));
    PrimWriteVtx(a, uv, col_upr_left);
    PrimWriteVtx(ImVec2(c.x, a.y), uv, col_upr_right);
    PrimWriteVtx(c, uv, col_bot_right);
    PrimWriteVtx(ImVec2(a.x, c.y), uv, col_bot_left);
}

void ImDrawList::AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c,
                         const ImVec2& d, ImU32 col, float thickness)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    PathLineTo(a);
    PathLineTo(b);
    PathLineTo(c);
    PathLineTo(d);
    PathStroke(col, true, thickness);
}

void ImDrawList::AddQuadFilled(const ImVec2& a, const ImVec2& b,
                               const ImVec2& c, const ImVec2& d, ImU32 col)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    PathLineTo(a);
    PathLineTo(b);
    PathLineTo(c);
    PathLineTo(d);
    PathFillConvex(col);
}

void ImDrawList::AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c,
                             ImU32 col, float thickness)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    PathLineTo(a);
    PathLineTo(b);
    PathLineTo(c);
    PathStroke(col, true, thickness);
}

void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b,
                                   const ImVec2& c, ImU32 col)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    PathLineTo(a);
    PathLineTo(b);
    PathLineTo(c);
    PathFillConvex(col);
}

void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col,
                           int num_segments, float thickness)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    const float a_max =
        IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
    PathArcTo(centre, radius - 0.5f, 0.0f, a_max, num_segments);
    PathStroke(col, true, thickness);
}

void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col,
                                 int num_segments)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    const float a_max =
        IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
    PathArcTo(centre, radius, 0.0f, a_max, num_segments);
    PathFillConvex(col);
}

void ImDrawList::AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0,
                                const ImVec2& cp1, const ImVec2& pos1,
                                ImU32 col, float thickness, int num_segments)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    PathLineTo(pos0);
    PathBezierCurveTo(cp0, cp1, pos1, num_segments);
    PathStroke(col, false, thickness);
}

void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos,
                         ImU32 col, const char* text_begin,
                         const char* text_end, float wrap_width,
                         const ImVec4* cpu_fine_clip_rect)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    if (text_end == NULL)
        text_end = text_begin + strlen(text_begin);
    if (text_begin == text_end)
        return;

    // Pull default font/size from the shared ImDrawListSharedData instance
    if (font == NULL)
        font = _Data->Font;
    if (font_size == 0.0f)
        font_size = _Data->FontSize;

    IM_ASSERT(font->ContainerAtlas->TexID ==
              _TextureIdStack
                  .back()); // Use high-level ImGui::PushFont() or low-level
                            // ImDrawList::PushTextureId() to change font.

    ImVec4 clip_rect = _ClipRectStack.back();
    if (cpu_fine_clip_rect)
    {
        clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x);
        clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y);
        clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
        clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
    }
    font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end,
                     wrap_width, cpu_fine_clip_rect != NULL);
}

void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin,
                         const char* text_end)
{
    AddText(NULL, 0.0f, pos, col, text_begin, text_end);
}

void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a,
                          const ImVec2& b, const ImVec2& uv_a,
                          const ImVec2& uv_b, ImU32 col)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    const bool push_texture_id =
        _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back();
    if (push_texture_id)
        PushTextureID(user_texture_id);

    PrimReserve(6, 4);
    PrimRectUV(a, b, uv_a, uv_b, col);

    if (push_texture_id)
        PopTextureID();
}

void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& a,
                              const ImVec2& b, const ImVec2& c, const ImVec2& d,
                              const ImVec2& uv_a, const ImVec2& uv_b,
                              const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    const bool push_texture_id =
        _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back();
    if (push_texture_id)
        PushTextureID(user_texture_id);

    PrimReserve(6, 4);
    PrimQuadUV(a, b, c, d, uv_a, uv_b, uv_c, uv_d, col);

    if (push_texture_id)
        PopTextureID();
}

void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& a,
                                 const ImVec2& b, const ImVec2& uv_a,
                                 const ImVec2& uv_b, ImU32 col, float rounding,
                                 int rounding_corners)
{
    if ((col & IM_COL32_A_MASK) == 0)
        return;

    if (rounding <= 0.0f || (rounding_corners & ImDrawCornerFlags_All) == 0)
    {
        AddImage(user_texture_id, a, b, uv_a, uv_b, col);
        return;
    }

    const bool push_texture_id =
        _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back();
    if (push_texture_id)
        PushTextureID(user_texture_id);

    int vert_start_idx = VtxBuffer.Size;
    PathRect(a, b, rounding, rounding_corners);
    PathFillConvex(col);
    int vert_end_idx = VtxBuffer.Size;
    ImGui::ShadeVertsLinearUV(VtxBuffer.Data + vert_start_idx,
                              VtxBuffer.Data + vert_end_idx, a, b, uv_a, uv_b,
                              true);

    if (push_texture_id)
        PopTextureID();
}

//-----------------------------------------------------------------------------
// ImDrawData
//-----------------------------------------------------------------------------

// For backward compatibility: convert all buffers from indexed to de-indexed,
// in case you cannot render indexed. Note: this is slow and most likely a waste
// of resources. Always prefer indexed rendering!
void ImDrawData::DeIndexAllBuffers()
{
    ImVector<ImDrawVert> new_vtx_buffer;
    TotalVtxCount = TotalIdxCount = 0;
    for (int i = 0; i < CmdListsCount; i++)
    {
        ImDrawList* cmd_list = CmdLists[i];
        if (cmd_list->IdxBuffer.empty())
            continue;
        new_vtx_buffer.resize(cmd_list->IdxBuffer.Size);
        for (int j = 0; j < cmd_list->IdxBuffer.Size; j++)
            new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]];
        cmd_list->VtxBuffer.swap(new_vtx_buffer);
        cmd_list->IdxBuffer.resize(0);
        TotalVtxCount += cmd_list->VtxBuffer.Size;
    }
}

// Helper to scale the ClipRect field of each ImDrawCmd. Use if your final
// output buffer is at a different scale than ImGui expects, or if there is a
// difference between your window resolution and framebuffer resolution.
void ImDrawData::ScaleClipRects(const ImVec2& scale)
{
    for (int i = 0; i < CmdListsCount; i++)
    {
        ImDrawList* cmd_list = CmdLists[i];
        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
        {
            ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i];
            cmd->ClipRect =
                ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y,
                       cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y);
        }
    }
}

//-----------------------------------------------------------------------------
// Shade functions
//-----------------------------------------------------------------------------

// Generic linear color gradient, write to RGB fields, leave A untouched.
void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start,
                                                   ImDrawVert* vert_end,
                                                   ImVec2      gradient_p0,
                                                   ImVec2      gradient_p1,
                                                   ImU32 col0, ImU32 col1)
{
    ImVec2 gradient_extent      = gradient_p1 - gradient_p0;
    float  gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent);
    for (ImDrawVert* vert = vert_start; vert < vert_end; vert++)
    {
        float d   = ImDot(vert->pos - gradient_p0, gradient_extent);
        float t   = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f);
        int   r   = ImLerp((int)(col0 >> IM_COL32_R_SHIFT) & 0xFF,
                       (int)(col1 >> IM_COL32_R_SHIFT) & 0xFF, t);
        int   g   = ImLerp((int)(col0 >> IM_COL32_G_SHIFT) & 0xFF,
                       (int)(col1 >> IM_COL32_G_SHIFT) & 0xFF, t);
        int   b   = ImLerp((int)(col0 >> IM_COL32_B_SHIFT) & 0xFF,
                       (int)(col1 >> IM_COL32_B_SHIFT) & 0xFF, t);
        vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) |
                    (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK);
    }
}

// Scan and shade backward from the end of given vertices. Assume vertices are
// text only (= vert_start..vert_end going left to right) so we can break as
// soon as we are out the gradient bounds.
void ImGui::ShadeVertsLinearAlphaGradientForLeftToRightText(
    ImDrawVert* vert_start, ImDrawVert* vert_end, float gradient_p0_x,
    float gradient_p1_x)
{
    float gradient_extent_x    = gradient_p1_x - gradient_p0_x;
    float gradient_inv_length2 = 1.0f / (gradient_extent_x * gradient_extent_x);
    int   full_alpha_count     = 0;
    for (ImDrawVert* vert = vert_end - 1; vert >= vert_start; vert--)
    {
        float d         = (vert->pos.x - gradient_p0_x) * (gradient_extent_x);
        float alpha_mul = 1.0f - ImClamp(d * gradient_inv_length2, 0.0f, 1.0f);
        if (alpha_mul >= 1.0f && ++full_alpha_count > 2)
            return; // Early out
        int a     = (int)(((vert->col >> IM_COL32_A_SHIFT) & 0xFF) * alpha_mul);
        vert->col = (vert->col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
    }
}

// Distribute UV over (a, b) rectangle
void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end,
                               const ImVec2& a, const ImVec2& b,
                               const ImVec2& uv_a, const ImVec2& uv_b,
                               bool clamp)
{
    const ImVec2 size    = b - a;
    const ImVec2 uv_size = uv_b - uv_a;
    const ImVec2 scale   = ImVec2(size.x != 0.0f ? (uv_size.x / size.x) : 0.0f,
                                size.y != 0.0f ? (uv_size.y / size.y) : 0.0f);

    if (clamp)
    {
        const ImVec2 min = ImMin(uv_a, uv_b);
        const ImVec2 max = ImMax(uv_a, uv_b);

        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
            vertex->uv = ImClamp(
                uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale),
                min, max);
    }
    else
    {
        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
            vertex->uv =
                uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale);
    }
}

//-----------------------------------------------------------------------------
// ImFontConfig
//-----------------------------------------------------------------------------

ImFontConfig::ImFontConfig()
{
    FontData             = NULL;
    FontDataSize         = 0;
    FontDataOwnedByAtlas = true;
    FontNo               = 0;
    SizePixels           = 0.0f;
    OversampleH          = 3;
    OversampleV          = 1;
    PixelSnapH           = false;
    GlyphExtraSpacing    = ImVec2(0.0f, 0.0f);
    GlyphOffset          = ImVec2(0.0f, 0.0f);
    GlyphRanges          = NULL;
    MergeMode            = false;
    RasterizerFlags      = 0x00;
    RasterizerMultiply   = 1.0f;
    memset(Name, 0, sizeof(Name));
    DstFont = NULL;
}

//-----------------------------------------------------------------------------
// ImFontAtlas
//-----------------------------------------------------------------------------

// A work of art lies ahead! (. = white layer, X = black layer, others are
// blank) The white texels on the top left are the ones we'll use everywhere in
// ImGui to render filled shapes.
const int          FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 90;
const int          FONT_ATLAS_DEFAULT_TEX_DATA_H      = 27;
const unsigned int FONT_ATLAS_DEFAULT_TEX_DATA_ID     = 0x80000000;
static const char  FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS
    [FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = {
        "..-         -XXXXXXX-    X    -           X           -XXXXXXX        "
        "  -          XXXXXXX"
        "..-         -X.....X-   X.X   -          X.X          -X.....X        "
        "  -          X.....X"
        "---         -XXX.XXX-  X...X  -         X...X         -X....X         "
        "  -           X....X"
        "X           -  X.X  - X.....X -        X.....X        -X...X          "
        "  -            X...X"
        "XX          -  X.X  -X.......X-       X.......X       -X..X.X         "
        "  -           X.X..X"
        "X.X         -  X.X  -XXXX.XXXX-       XXXX.XXXX       -X.X X.X        "
        "  -          X.X X.X"
        "X..X        -  X.X  -   X.X   -          X.X          -XX   X.X       "
        "  -         X.X   XX"
        "X...X       -  X.X  -   X.X   -    XX    X.X    XX    -      X.X      "
        "  -        X.X      "
        "X....X      -  X.X  -   X.X   -   X.X    X.X    X.X   -       X.X     "
        "  -       X.X       "
        "X.....X     -  X.X  -   X.X   -  X..X    X.X    X..X  -        X.X    "
        "  -      X.X        "
        "X......X    -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -         X.X   "
        "XX-XX   X.X         "
        "X.......X   -  X.X  -   X.X   -X.....................X-          X.X "
        "X.X-X.X X.X          "
        "X........X  -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -           "
        "X.X..X-X..X.X           "
        "X.........X -XXX.XXX-   X.X   -  X..X    X.X    X..X  -            "
        "X...X-X...X            "
        "X..........X-X.....X-   X.X   -   X.X    X.X    X.X   -           "
        "X....X-X....X           "
        "X......XXXXX-XXXXXXX-   X.X   -    XX    X.X    XX    -          "
        "X.....X-X.....X          "
        "X...X..X    ---------   X.X   -          X.X          -          "
        "XXXXXXX-XXXXXXX          "
        "X..X X..X   -       -XXXX.XXXX-       XXXX.XXXX       "
        "------------------------------------"
        "X.X  X..X   -       -X.......X-       X.......X       -    XX         "
        "  XX    -           "
        "XX    X..X  -       - X.....X -        X.....X        -   X.X         "
        "  X.X   -           "
        "      X..X          -  X...X  -         X...X         -  X..X         "
        "  X..X  -           "
        "       XX           -   X.X   -          X.X          - "
        "X...XXXXXXXXXXXXX...X -           "
        "------------        -    X    -           X           "
        "-X.....................X-           "
        "                    ----------------------------------- "
        "X...XXXXXXXXXXXXX...X -           "
        "                                                      -  X..X         "
        "  X..X  -           "
        "                                                      -   X.X         "
        "  X.X   -           "
        "                                                      -    XX         "
        "  XX    -           "
    };

static const ImVec2
    FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_Count_][3] = {
        // Pos ........ Size ......... Offset ......
        { ImVec2(0, 3), ImVec2(12, 19),
          ImVec2(0, 0) }, // ImGuiMouseCursor_Arrow
        { ImVec2(13, 0), ImVec2(7, 16),
          ImVec2(4, 8) }, // ImGuiMouseCursor_TextInput
        { ImVec2(31, 0), ImVec2(23, 23),
          ImVec2(11, 11) }, // ImGuiMouseCursor_Move
        { ImVec2(21, 0), ImVec2(9, 23),
          ImVec2(5, 11) }, // ImGuiMouseCursor_ResizeNS
        { ImVec2(55, 18), ImVec2(23, 9),
          ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW
        { ImVec2(73, 0), ImVec2(17, 17),
          ImVec2(9, 9) }, // ImGuiMouseCursor_ResizeNESW
        { ImVec2(55, 0), ImVec2(17, 17),
          ImVec2(9, 9) }, // ImGuiMouseCursor_ResizeNWSE
    };

ImFontAtlas::ImFontAtlas()
{
    TexID           = NULL;
    TexDesiredWidth = 0;
    TexGlyphPadding = 1;
    TexPixelsAlpha8 = NULL;
    TexPixelsRGBA32 = NULL;
    TexWidth = TexHeight = 0;
    TexUvWhitePixel      = ImVec2(0, 0);
    for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++)
        CustomRectIds[n] = -1;
}

ImFontAtlas::~ImFontAtlas()
{
    Clear();
}

void ImFontAtlas::ClearInputData()
{
    for (int i = 0; i < ConfigData.Size; i++)
        if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas)
        {
            ImGui::MemFree(ConfigData[i].FontData);
            ConfigData[i].FontData = NULL;
        }

    // When clearing this we lose access to  the font name and other information
    // used to build the font.
    for (int i = 0; i < Fonts.Size; i++)
        if (Fonts[i]->ConfigData >= ConfigData.Data &&
            Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size)
        {
            Fonts[i]->ConfigData      = NULL;
            Fonts[i]->ConfigDataCount = 0;
        }
    ConfigData.clear();
    CustomRects.clear();
    for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++)
        CustomRectIds[n] = -1;
}

void ImFontAtlas::ClearTexData()
{
    if (TexPixelsAlpha8)
        ImGui::MemFree(TexPixelsAlpha8);
    if (TexPixelsRGBA32)
        ImGui::MemFree(TexPixelsRGBA32);
    TexPixelsAlpha8 = NULL;
    TexPixelsRGBA32 = NULL;
}

void ImFontAtlas::ClearFonts()
{
    for (int i = 0; i < Fonts.Size; i++)
        IM_DELETE(Fonts[i]);
    Fonts.clear();
}

void ImFontAtlas::Clear()
{
    ClearInputData();
    ClearTexData();
    ClearFonts();
}

void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width,
                                     int* out_height, int* out_bytes_per_pixel)
{
    // Build atlas on demand
    if (TexPixelsAlpha8 == NULL)
    {
        if (ConfigData.empty())
            AddFontDefault();
        Build();
    }

    *out_pixels = TexPixelsAlpha8;
    if (out_width)
        *out_width = TexWidth;
    if (out_height)
        *out_height = TexHeight;
    if (out_bytes_per_pixel)
        *out_bytes_per_pixel = 1;
}

void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width,
                                     int* out_height, int* out_bytes_per_pixel)
{
    // Convert to RGBA32 format on demand
    // Although it is likely to be the most commonly used format, our font
    // rendering is 1 channel / 8 bpp
    if (!TexPixelsRGBA32)
    {
        unsigned char* pixels = NULL;
        GetTexDataAsAlpha8(&pixels, NULL, NULL);
        if (pixels)
        {
            TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc(
                (size_t)(TexWidth * TexHeight * 4));
            const unsigned char* src = pixels;
            unsigned int*        dst = TexPixelsRGBA32;
            for (int n = TexWidth * TexHeight; n > 0; n--)
                *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++));
        }
    }

    *out_pixels = (unsigned char*)TexPixelsRGBA32;
    if (out_width)
        *out_width = TexWidth;
    if (out_height)
        *out_height = TexHeight;
    if (out_bytes_per_pixel)
        *out_bytes_per_pixel = 4;
}

ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
{
    IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);
    IM_ASSERT(font_cfg->SizePixels > 0.0f);

    // Create new font
    if (!font_cfg->MergeMode)
        Fonts.push_back(IM_NEW(ImFont));
    else
        IM_ASSERT(!Fonts.empty()); // When using MergeMode make sure that a font
                                   // has already been added before. You can use
                                   // ImGui::GetIO().Fonts->AddFontDefault() to
                                   // add the default imgui font.

    ConfigData.push_back(*font_cfg);
    ImFontConfig& new_font_cfg = ConfigData.back();
    if (!new_font_cfg.DstFont)
        new_font_cfg.DstFont = Fonts.back();
    if (!new_font_cfg.FontDataOwnedByAtlas)
    {
        new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize);
        new_font_cfg.FontDataOwnedByAtlas = true;
        memcpy(new_font_cfg.FontData, font_cfg->FontData,
               (size_t)new_font_cfg.FontDataSize);
    }

    // Invalidate texture
    ClearTexData();
    return new_font_cfg.DstFont;
}

// Default font TTF is compressed with stb_compress then base85 encoded (see
// extra_fonts/binary_to_compressed_c.cpp for encoder)
static unsigned int stb_decompress_length(unsigned char* input);
static unsigned int stb_decompress(unsigned char* output, unsigned char* i,
                                   unsigned int length);
static const char*  GetDefaultCompressedFontDataTTFBase85();
static unsigned int Decode85Byte(char c)
{
    return c >= '\\' ? c - 36 : c - 35;
}
static void Decode85(const unsigned char* src, unsigned char* dst)
{
    while (*src)
    {
        unsigned int tmp = Decode85Byte(src[0]) +
                           85 * (Decode85Byte(src[1]) +
                                 85 * (Decode85Byte(src[2]) +
                                       85 * (Decode85Byte(src[3]) +
                                             85 * Decode85Byte(src[4]))));
        dst[0] = ((tmp >> 0) & 0xFF);
        dst[1] = ((tmp >> 8) & 0xFF);
        dst[2] = ((tmp >> 16) & 0xFF);
        dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness.
        src += 5;
        dst += 4;
    }
}

// Load embedded ProggyClean.ttf at size 13, disable oversampling
ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
{
    ImFontConfig font_cfg =
        font_cfg_template ? *font_cfg_template : ImFontConfig();
    if (!font_cfg_template)
    {
        font_cfg.OversampleH = font_cfg.OversampleV = 1;
        font_cfg.PixelSnapH                         = true;
    }
    if (font_cfg.Name[0] == '\0')
        strcpy(font_cfg.Name, "ProggyClean.ttf, 13px");
    if (font_cfg.SizePixels <= 0.0f)
        font_cfg.SizePixels = 13.0f;

    const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
    ImFont*     font                  = AddFontFromMemoryCompressedBase85TTF(
        ttf_compressed_base85, font_cfg.SizePixels, &font_cfg,
        GetGlyphRangesDefault());
    return font;
}

ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
                                        const ImFontConfig* font_cfg_template,
                                        const ImWchar*      glyph_ranges)
{
    int   data_size = 0;
    void* data      = ImFileLoadToMemory(filename, "rb", &data_size, 0);
    if (!data)
    {
        IM_ASSERT(0); // Could not load file.
        return NULL;
    }
    ImFontConfig font_cfg =
        font_cfg_template ? *font_cfg_template : ImFontConfig();
    if (font_cfg.Name[0] == '\0')
    {
        // Store a short copy of filename into into the font name for
        // convenience
        const char* p;
        for (p = filename + strlen(filename);
             p > filename && p[-1] != '/' && p[-1] != '\\'; p--)
        {
        }
        snprintf(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p,
                 size_pixels);
    }
    return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg,
                                glyph_ranges);
}

// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless
// font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be
// deleted after Build().
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size,
                                          float               size_pixels,
                                          const ImFontConfig* font_cfg_template,
                                          const ImWchar*      glyph_ranges)
{
    ImFontConfig font_cfg =
        font_cfg_template ? *font_cfg_template : ImFontConfig();
    IM_ASSERT(font_cfg.FontData == NULL);
    font_cfg.FontData     = ttf_data;
    font_cfg.FontDataSize = ttf_size;
    font_cfg.SizePixels   = size_pixels;
    if (glyph_ranges)
        font_cfg.GlyphRanges = glyph_ranges;
    return AddFont(&font_cfg);
}

ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(
    const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels,
    const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
{
    const unsigned int buf_decompressed_size =
        stb_decompress_length((unsigned char*)compressed_ttf_data);
    unsigned char* buf_decompressed_data =
        (unsigned char*)ImGui::MemAlloc(buf_decompressed_size);
    stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data,
                   (unsigned int)compressed_ttf_size);

    ImFontConfig font_cfg =
        font_cfg_template ? *font_cfg_template : ImFontConfig();
    IM_ASSERT(font_cfg.FontData == NULL);
    font_cfg.FontDataOwnedByAtlas = true;
    return AddFontFromMemoryTTF(buf_decompressed_data,
                                (int)buf_decompressed_size, size_pixels,
                                &font_cfg, glyph_ranges);
}

ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(
    const char* compressed_ttf_data_base85, float size_pixels,
    const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)
{
    int compressed_ttf_size =
        (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4;
    void* compressed_ttf = ImGui::MemAlloc((size_t)compressed_ttf_size);
    Decode85((const unsigned char*)compressed_ttf_data_base85,
             (unsigned char*)compressed_ttf);
    ImFont* font =
        AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size,
                                       size_pixels, font_cfg, glyph_ranges);
    ImGui::MemFree(compressed_ttf);
    return font;
}

int ImFontAtlas::AddCustomRectRegular(unsigned int id, int width, int height)
{
    IM_ASSERT(id >= 0x10000);
    IM_ASSERT(width > 0 && width <= 0xFFFF);
    IM_ASSERT(height > 0 && height <= 0xFFFF);
    CustomRect r;
    r.ID     = id;
    r.Width  = (unsigned short)width;
    r.Height = (unsigned short)height;
    CustomRects.push_back(r);
    return CustomRects.Size - 1; // Return index
}

int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width,
                                        int height, float advance_x,
                                        const ImVec2& offset)
{
    IM_ASSERT(font != NULL);
    IM_ASSERT(width > 0 && width <= 0xFFFF);
    IM_ASSERT(height > 0 && height <= 0xFFFF);
    CustomRect r;
    r.ID            = id;
    r.Width         = (unsigned short)width;
    r.Height        = (unsigned short)height;
    r.GlyphAdvanceX = advance_x;
    r.GlyphOffset   = offset;
    r.Font          = font;
    CustomRects.push_back(r);
    return CustomRects.Size - 1; // Return index
}

void ImFontAtlas::CalcCustomRectUV(const CustomRect* rect, ImVec2* out_uv_min,
                                   ImVec2* out_uv_max)
{
    IM_ASSERT(TexWidth > 0 &&
              TexHeight > 0);    // Font atlas needs to be built before we can
                                 // calculate UV coordinates
    IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed
    *out_uv_min = ImVec2((float)rect->X / TexWidth, (float)rect->Y / TexHeight);
    *out_uv_max = ImVec2((float)(rect->X + rect->Width) / TexWidth,
                         (float)(rect->Y + rect->Height) / TexHeight);
}

bool ImFontAtlas::Build()
{
    return ImFontAtlasBuildWithStbTruetype(this);
}

void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256],
                                             float         in_brighten_factor)
{
    for (unsigned int i = 0; i < 256; i++)
    {
        unsigned int value = (unsigned int)(i * in_brighten_factor);
        out_table[i]       = value > 255 ? 255 : (value & 0xFF);
    }
}

void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256],
                                        unsigned char* pixels, int x, int y,
                                        int w, int h, int stride)
{
    unsigned char* data = pixels + x + y * stride;
    for (int j = h; j > 0; j--, data += stride)
        for (int i = 0; i < w; i++)
            data[i] = table[data[i]];
}

bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
{
    IM_ASSERT(atlas->ConfigData.Size > 0);

    ImFontAtlasBuildRegisterDefaultCustomRects(atlas);

    atlas->TexID    = NULL;
    atlas->TexWidth = atlas->TexHeight = 0;
    atlas->TexUvWhitePixel             = ImVec2(0, 0);
    atlas->ClearTexData();

    // Count glyphs/ranges
    int total_glyphs_count = 0;
    int total_ranges_count = 0;
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
        ImFontConfig& cfg = atlas->ConfigData[input_i];
        if (!cfg.GlyphRanges)
            cfg.GlyphRanges = atlas->GetGlyphRangesDefault();
        for (const ImWchar* in_range = cfg.GlyphRanges;
             in_range[0] && in_range[1]; in_range += 2, total_ranges_count++)
            total_glyphs_count += (in_range[1] - in_range[0]) + 1;
    }

    // We need a width for the skyline algorithm. Using a dumb heuristic here to
    // decide of width. User can override TexDesiredWidth and TexGlyphPadding if
    // they wish. Width doesn't really matter much, but some API/GPU have
    // texture size limitations and increasing width can decrease height.
    atlas->TexWidth =
        (atlas->TexDesiredWidth > 0)
            ? atlas->TexDesiredWidth
            : (total_glyphs_count > 4000)
                  ? 4096
                  : (total_glyphs_count > 2000)
                        ? 2048
                        : (total_glyphs_count > 1000) ? 1024 : 512;
    atlas->TexHeight = 0;

    // Start packing
    const int          max_tex_height = 1024 * 32;
    stbtt_pack_context spc            = {};
    stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0,
                    atlas->TexGlyphPadding, NULL);
    stbtt_PackSetOversampling(&spc, 1, 1);

    // Pack our extra data rectangles first, so it will be on the upper-left
    // corner of our texture (UV will have small values).
    ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);

    // Initialize font information (so we can error without any cleanup)
    struct ImFontTempBuildData
    {
        stbtt_fontinfo    FontInfo;
        stbrp_rect*       Rects;
        int               RectsCount;
        stbtt_pack_range* Ranges;
        int               RangesCount;
    };
    ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc(
        (size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData));
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
        ImFontConfig&        cfg = atlas->ConfigData[input_i];
        ImFontTempBuildData& tmp = tmp_array[input_i];
        IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() ||
                                  cfg.DstFont->ContainerAtlas == atlas));

        const int font_offset = stbtt_GetFontOffsetForIndex(
            (unsigned char*)cfg.FontData, cfg.FontNo);
        IM_ASSERT(font_offset >= 0);
        if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData,
                            font_offset))
        {
            atlas->TexWidth = atlas->TexHeight = 0; // Reset output on failure
            ImGui::MemFree(tmp_array);
            return false;
        }
    }

    // Allocate packing character data and flag packed characters buffer as
    // non-packed (x0=y0=x1=y1=0)
    int               buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0;
    stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(
        total_glyphs_count * sizeof(stbtt_packedchar));
    stbrp_rect* buf_rects =
        (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect));
    stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(
        total_ranges_count * sizeof(stbtt_pack_range));
    memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar));
    memset(buf_rects, 0,
           total_glyphs_count *
               sizeof(stbrp_rect)); // Unnecessary but let's clear this for the
                                    // sake of sanity.
    memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range));

    // First font pass: pack all glyphs (no rendering at this point, we are
    // working with rectangles in an infinitely tall texture at this point)
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
        ImFontConfig&        cfg = atlas->ConfigData[input_i];
        ImFontTempBuildData& tmp = tmp_array[input_i];

        // Setup ranges
        int font_glyphs_count = 0;
        int font_ranges_count = 0;
        for (const ImWchar* in_range = cfg.GlyphRanges;
             in_range[0] && in_range[1]; in_range += 2, font_ranges_count++)
            font_glyphs_count += (in_range[1] - in_range[0]) + 1;
        tmp.Ranges      = buf_ranges + buf_ranges_n;
        tmp.RangesCount = font_ranges_count;
        buf_ranges_n += font_ranges_count;
        for (int i = 0; i < font_ranges_count; i++)
        {
            const ImWchar*    in_range             = &cfg.GlyphRanges[i * 2];
            stbtt_pack_range& range                = tmp.Ranges[i];
            range.font_size                        = cfg.SizePixels;
            range.first_unicode_codepoint_in_range = in_range[0];
            range.num_chars          = (in_range[1] - in_range[0]) + 1;
            range.chardata_for_range = buf_packedchars + buf_packedchars_n;
            buf_packedchars_n += range.num_chars;
        }

        // Pack
        tmp.Rects      = buf_rects + buf_rects_n;
        tmp.RectsCount = font_glyphs_count;
        buf_rects_n += font_glyphs_count;
        stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
        int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges,
                                                tmp.RangesCount, tmp.Rects);
        IM_ASSERT(n == font_glyphs_count);
        stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);

        // Extend texture height
        for (int i = 0; i < n; i++)
            if (tmp.Rects[i].was_packed)
                atlas->TexHeight =
                    ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
    }
    IM_ASSERT(buf_rects_n == total_glyphs_count);
    IM_ASSERT(buf_packedchars_n == total_glyphs_count);
    IM_ASSERT(buf_ranges_n == total_ranges_count);

    // Create texture
    atlas->TexHeight = ImUpperPowerOfTwo(atlas->TexHeight);
    atlas->TexPixelsAlpha8 =
        (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
    memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
    spc.pixels = atlas->TexPixelsAlpha8;
    spc.height = atlas->TexHeight;

    // Second pass: render font characters
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
        ImFontConfig&        cfg = atlas->ConfigData[input_i];
        ImFontTempBuildData& tmp = tmp_array[input_i];
        stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
        stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges,
                                            tmp.RangesCount, tmp.Rects);
        if (cfg.RasterizerMultiply != 1.0f)
        {
            unsigned char multiply_table[256];
            ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table,
                                                    cfg.RasterizerMultiply);
            for (const stbrp_rect* r = tmp.Rects;
                 r != tmp.Rects + tmp.RectsCount; r++)
                if (r->was_packed)
                    ImFontAtlasBuildMultiplyRectAlpha8(
                        multiply_table, spc.pixels, r->x, r->y, r->w, r->h,
                        spc.stride_in_bytes);
        }
        tmp.Rects = NULL;
    }

    // End packing
    stbtt_PackEnd(&spc);
    ImGui::MemFree(buf_rects);
    buf_rects = NULL;

    // Third pass: setup ImFont and glyphs for runtime
    for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
    {
        ImFontConfig&        cfg = atlas->ConfigData[input_i];
        ImFontTempBuildData& tmp = tmp_array[input_i];
        ImFont*              dst_font =
            cfg.DstFont; // We can have multiple input fonts writing into a same
                         // destination font (when using MergeMode=true)

        const float font_scale =
            stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels);
        int unscaled_ascent, unscaled_descent, unscaled_line_gap;
        stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent,
                              &unscaled_descent, &unscaled_line_gap);

        const float ascent  = unscaled_ascent * font_scale;
        const float descent = unscaled_descent * font_scale;
        ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
        const float off_x = cfg.GlyphOffset.x;
        const float off_y =
            cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f);

        for (int i = 0; i < tmp.RangesCount; i++)
        {
            stbtt_pack_range& range = tmp.Ranges[i];
            for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1)
            {
                const stbtt_packedchar& pc = range.chardata_for_range[char_idx];
                if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1)
                    continue;

                const int codepoint =
                    range.first_unicode_codepoint_in_range + char_idx;
                if (cfg.MergeMode &&
                    dst_font->FindGlyph((unsigned short)codepoint))
                    continue;

                stbtt_aligned_quad q;
                float              dummy_x = 0.0f, dummy_y = 0.0f;
                stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth,
                                    atlas->TexHeight, char_idx, &dummy_x,
                                    &dummy_y, &q, 0);
                dst_font->AddGlyph((ImWchar)codepoint, q.x0 + off_x,
                                   q.y0 + off_y, q.x1 + off_x, q.y1 + off_y,
                                   q.s0, q.t0, q.s1, q.t1, pc.xadvance);
            }
        }
    }

    // Cleanup temporaries
    ImGui::MemFree(buf_packedchars);
    ImGui::MemFree(buf_ranges);
    ImGui::MemFree(tmp_array);

    ImFontAtlasBuildFinish(atlas);

    return true;
}

void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas)
{
    if (atlas->CustomRectIds[0] < 0)
        atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(
            FONT_ATLAS_DEFAULT_TEX_DATA_ID,
            FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1,
            FONT_ATLAS_DEFAULT_TEX_DATA_H);
}

void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font,
                               ImFontConfig* font_config, float ascent,
                               float descent)
{
    if (!font_config->MergeMode)
    {
        font->ClearOutputData();
        font->FontSize       = font_config->SizePixels;
        font->ConfigData     = font_config;
        font->ContainerAtlas = atlas;
        font->Ascent         = ascent;
        font->Descent        = descent;
    }
    font->ConfigDataCount++;
}

void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas,
                                     void*        pack_context_opaque)
{
    stbrp_context* pack_context = (stbrp_context*)pack_context_opaque;

    ImVector<ImFontAtlas::CustomRect>& user_rects = atlas->CustomRects;
    IM_ASSERT(user_rects.Size >=
              1); // We expect at least the default custom rects to be
                  // registered, else something went wrong.

    ImVector<stbrp_rect> pack_rects;
    pack_rects.resize(user_rects.Size);
    memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size);
    for (int i = 0; i < user_rects.Size; i++)
    {
        pack_rects[i].w = user_rects[i].Width;
        pack_rects[i].h = user_rects[i].Height;
    }
    stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size);
    for (int i = 0; i < pack_rects.Size; i++)
        if (pack_rects[i].was_packed)
        {
            user_rects[i].X = pack_rects[i].x;
            user_rects[i].Y = pack_rects[i].y;
            IM_ASSERT(pack_rects[i].w == user_rects[i].Width &&
                      pack_rects[i].h == user_rects[i].Height);
            atlas->TexHeight =
                ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h);
        }
}

static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
{
    IM_ASSERT(atlas->CustomRectIds[0] >= 0);
    ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->CustomRectIds[0]];
    IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID);
    IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1);
    IM_ASSERT(r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);
    IM_ASSERT(r.IsPacked());
    IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);

    // Render/copy pixels
    for (int y = 0, n = 0; y < FONT_ATLAS_DEFAULT_TEX_DATA_H; y++)
        for (int x = 0; x < FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF; x++, n++)
        {
            const int offset0 =
                (int)(r.X + x) + (int)(r.Y + y) * atlas->TexWidth;
            const int offset1 =
                offset0 + FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
            atlas->TexPixelsAlpha8[offset0] =
                FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == '.' ? 0xFF : 0x00;
            atlas->TexPixelsAlpha8[offset1] =
                FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == 'X' ? 0xFF : 0x00;
        }
    const ImVec2 tex_uv_scale(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
    atlas->TexUvWhitePixel =
        ImVec2((r.X + 0.5f) * tex_uv_scale.x, (r.Y + 0.5f) * tex_uv_scale.y);

    // Setup mouse cursors
    for (int type = 0; type < ImGuiMouseCursor_Count_; type++)
    {
        ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type];
        ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[type][0] +
                     ImVec2((float)r.X, (float)r.Y);
        const ImVec2 size       = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[type][1];
        cursor_data.Type        = type;
        cursor_data.Size        = size;
        cursor_data.HotOffset   = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[type][2];
        cursor_data.TexUvMin[0] = (pos)*tex_uv_scale;
        cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale;
        pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
        cursor_data.TexUvMin[1] = (pos)*tex_uv_scale;
        cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale;
    }
}

void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
{
    // Render into our custom data block
    ImFontAtlasBuildRenderDefaultTexData(atlas);

    // Register custom rectangle glyphs
    for (int i = 0; i < atlas->CustomRects.Size; i++)
    {
        const ImFontAtlas::CustomRect& r = atlas->CustomRects[i];
        if (r.Font == NULL || r.ID > 0x10000)
            continue;

        IM_ASSERT(r.Font->ContainerAtlas == atlas);
        ImVec2 uv0, uv1;
        atlas->CalcCustomRectUV(&r, &uv0, &uv1);
        r.Font->AddGlyph((ImWchar)r.ID, r.GlyphOffset.x, r.GlyphOffset.y,
                         r.GlyphOffset.x + r.Width, r.GlyphOffset.y + r.Height,
                         uv0.x, uv0.y, uv1.x, uv1.y, r.GlyphAdvanceX);
    }

    // Build all fonts lookup tables
    for (int i = 0; i < atlas->Fonts.Size; i++)
        atlas->Fonts[i]->BuildLookupTable();
}

// Retrieve list of range (2 int per range, values are inclusive)
const ImWchar* ImFontAtlas::GetGlyphRangesDefault()
{
    static const ImWchar ranges[] = {
        0x0020,
        0x00FF, // Basic Latin + Latin Supplement
        0,
    };
    return &ranges[0];
}

const ImWchar* ImFontAtlas::GetGlyphRangesKorean()
{
    static const ImWchar ranges[] = {
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
        0x3131, 0x3163, // Korean alphabets
        0xAC00, 0xD79D, // Korean characters
        0,
    };
    return &ranges[0];
}

const ImWchar* ImFontAtlas::GetGlyphRangesChinese()
{
    static const ImWchar ranges[] = {
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
        0x3000, 0x30FF, // Punctuations, Hiragana, Katakana
        0x31F0, 0x31FF, // Katakana Phonetic Extensions
        0xFF00, 0xFFEF, // Half-width characters
        0x4e00, 0x9FAF, // CJK Ideograms
        0,
    };
    return &ranges[0];
}

const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
{
    // Store the 1946 ideograms code points as successive offsets from the
    // initial unicode codepoint 0x4E00. Each offset has an implicit +1. This
    // encoding is designed to helps us reduce the source code size.
    // FIXME: Source a list of the revised 2136 joyo kanji list from 2010 and
    // rebuild this. The current list was sourced from
    // http://theinstructionlimit.com/author/renaudbedardrenaudbedard/page/3
    // Note that you may use ImFontAtlas::GlyphRangesBuilder to create your own
    // ranges, by merging existing ranges or adding new characters.
    static const short offsets_from_0x4E00[] = {
        -1, 0,   1,   3,  0,   0,  0,   0,  1,  0,  5,   1,   1,  0,   7,
        4,  6,   10,  0,  1,   9,  9,   7,  1,  3,  19,  1,   10, 7,   1,
        0,  1,   0,   5,  1,   0,  6,   4,  2,  6,  0,   0,   12, 6,   8,
        0,  3,   5,   0,  1,   0,  9,   0,  0,  8,  1,   1,   3,  4,   5,
        13, 0,   0,   8,  2,   17, 4,   3,  1,  1,  9,   6,   0,  0,   0,
        2,  1,   3,   2,  22,  1,  9,   11, 1,  13, 1,   3,   12, 0,   5,
        9,  2,   0,   6,  12,  5,  3,   12, 4,  1,  2,   16,  1,  1,   4,
        6,  5,   3,   0,  6,   13, 15,  5,  12, 8,  14,  0,   0,  6,   15,
        3,  6,   0,   18, 8,   1,  6,   14, 1,  5,  4,   12,  24, 3,   13,
        12, 10,  24,  0,  0,   0,  1,   0,  1,  1,  2,   9,   10, 2,   2,
        0,  0,   3,   3,  1,   0,  3,   8,  0,  3,  2,   4,   4,  1,   6,
        11, 10,  14,  6,  15,  3,  4,   15, 1,  0,  0,   5,   2,  2,   0,
        0,  1,   6,   5,  5,   6,  0,   3,  6,  5,  0,   0,   1,  0,   11,
        2,  2,   8,   4,  7,   0,  10,  0,  1,  2,  17,  19,  3,  0,   2,
        5,  0,   6,   2,  4,   4,  6,   1,  1,  11, 2,   0,   3,  1,   2,
        1,  2,   10,  7,  6,   3,  16,  0,  8,  24, 0,   0,   3,  1,   1,
        3,  0,   1,   6,  0,   0,  0,   2,  0,  1,  5,   15,  0,  1,   0,
        0,  2,   11,  19, 1,   4,  19,  7,  6,  5,  1,   0,   0,  0,   0,
        5,  1,   0,   1,  9,   0,  0,   5,  0,  2,  0,   1,   0,  3,   0,
        11, 3,   0,   2,  0,   0,  0,   0,  0,  9,  3,   6,   4,  12,  0,
        14, 0,   0,   29, 10,  8,  0,   14, 37, 13, 0,   31,  16, 19,  0,
        8,  30,  1,   20, 8,   3,  48,  21, 1,  0,  12,  0,   10, 44,  34,
        42, 54,  11,  18, 82,  0,  2,   1,  2,  12, 1,   0,   6,  2,   17,
        2,  12,  7,   0,  7,   17, 4,   2,  6,  24, 23,  8,   23, 39,  2,
        16, 23,  1,   0,  5,   1,  2,   15, 14, 5,  6,   2,   11, 0,   8,
        6,  2,   2,   2,  14,  20, 4,   15, 3,  4,  11,  10,  10, 2,   5,
        2,  1,   30,  2,  1,   0,  0,   22, 5,  5,  0,   3,   1,  5,   4,
        1,  0,   0,   2,  2,   21, 1,   5,  1,  2,  16,  2,   1,  3,   4,
        0,  8,   4,   0,  0,   5,  14,  11, 2,  16, 1,   13,  1,  7,   0,
        22, 15,  3,   1,  22,  7,  14,  22, 19, 11, 24,  18,  46, 10,  20,
        64, 45,  3,   2,  0,   4,  5,   0,  1,  4,  25,  1,   0,  0,   2,
        10, 0,   0,   0,  1,   0,  1,   2,  0,  0,  9,   1,   2,  0,   0,
        0,  2,   5,   2,  1,   1,  5,   5,  8,  1,  1,   1,   5,  1,   4,
        9,  1,   3,   0,  1,   0,  1,   1,  2,  0,  0,   2,   0,  1,   8,
        22, 8,   1,   0,  0,   0,  0,   4,  2,  1,  0,   9,   8,  5,   0,
        9,  1,   30,  24, 2,   6,  4,   39, 0,  14, 5,   16,  6,  26,  179,
        0,  2,   1,   1,  0,   0,  0,   5,  2,  9,  6,   0,   2,  5,   16,
        7,  5,   1,   1,  0,   2,  4,   4,  7,  15, 13,  14,  0,  0,   3,
        0,  1,   0,   0,  0,   2,  1,   6,  4,  5,  1,   4,   9,  0,   3,
        1,  8,   0,   0,  10,  5,  0,   43, 0,  2,  6,   8,   4,  0,   2,
        0,  0,   9,   6,  0,   9,  3,   1,  6,  20, 14,  6,   1,  4,   0,
        7,  2,   3,   0,  2,   0,  5,   0,  3,  1,  0,   3,   9,  7,   0,
        3,  4,   0,   4,  9,   1,  6,   0,  9,  0,  0,   2,   3,  10,  9,
        28, 3,   6,   2,  4,   1,  2,   32, 4,  1,  18,  2,   0,  3,   1,
        5,  30,  10,  0,  2,   2,  2,   0,  7,  9,  8,   11,  10, 11,  7,
        2,  13,  7,   5,  10,  0,  3,   40, 2,  0,  1,   6,   12, 0,   4,
        5,  1,   5,   11, 11,  21, 4,   8,  3,  7,  8,   8,   33, 5,   23,
        0,  0,   19,  8,  8,   2,  3,   0,  6,  1,  1,   1,   5,  1,   27,
        4,  2,   5,   0,  3,   5,  6,   3,  1,  0,  3,   1,   12, 5,   3,
        3,  2,   0,   7,  7,   2,  1,   0,  4,  0,  1,   1,   2,  0,   10,
        10, 6,   2,   5,  9,   7,  5,   15, 15, 21, 6,   11,  5,  20,  4,
        3,  5,   5,   2,  5,   0,  2,   1,  0,  1,  7,   28,  0,  9,   0,
        5,  12,  5,   5,  18,  30, 0,   12, 3,  3,  21,  16,  25, 32,  9,
        3,  14,  11,  24, 5,   66, 9,   1,  2,  0,  5,   9,   1,  5,   1,
        8,  0,   8,   3,  3,   0,  1,   15, 1,  4,  8,   1,   2,  7,   0,
        7,  2,   8,   3,  7,   5,  3,   7,  10, 2,  1,   0,   0,  2,   25,
        0,  6,   4,   0,  10,  0,  4,   2,  4,  1,  12,  5,   38, 4,   0,
        4,  1,   10,  5,  9,   4,  0,   14, 4,  2,  5,   18,  20, 21,  1,
        3,  0,   5,   0,  7,   0,  3,   7,  1,  3,  1,   1,   8,  1,   0,
        0,  0,   3,   2,  5,   2,  11,  6,  0,  13, 1,   3,   9,  1,   12,
        0,  16,  6,   2,  1,   0,  2,   1,  12, 6,  13,  11,  2,  0,   28,
        1,  7,   8,   14, 13,  8,  13,  0,  2,  0,  5,   4,   8,  10,  2,
        37, 42,  19,  6,  6,   7,  4,   14, 11, 18, 14,  80,  7,  6,   0,
        4,  72,  12,  36, 27,  7,  7,   0,  14, 17, 19,  164, 27, 0,   5,
        10, 7,   3,   13, 6,   14, 0,   2,  2,  5,  3,   0,   6,  13,  0,
        0,  10,  29,  0,  4,   0,  3,   13, 0,  3,  1,   6,   51, 1,   5,
        28, 2,   0,   8,  0,   20, 2,   4,  0,  25, 2,   10,  13, 10,  0,
        16, 4,   0,   1,  0,   2,  1,   7,  0,  1,  8,   11,  0,  0,   1,
        2,  7,   2,   23, 11,  6,  6,   4,  16, 2,  2,   2,   0,  22,  9,
        3,  3,   5,   2,  0,   15, 16,  21, 2,  9,  20,  15,  15, 5,   3,
        9,  1,   0,   0,  1,   7,  7,   5,  4,  2,  2,   2,   38, 24,  14,
        0,  0,   15,  5,  6,   24, 14,  5,  5,  11, 0,   21,  12, 0,   3,
        8,  4,   11,  1,  8,   0,  11,  27, 7,  2,  4,   9,   21, 59,  0,
        1,  39,  3,   60, 62,  3,  0,   12, 11, 0,  3,   30,  11, 0,   13,
        88, 4,   15,  5,  28,  13, 1,   4,  48, 17, 17,  4,   28, 32,  46,
        0,  16,  0,   18, 11,  1,  8,   6,  38, 11, 2,   6,   11, 38,  2,
        0,  45,  3,   11, 2,   7,  8,   4,  30, 14, 17,  2,   1,  1,   65,
        18, 12,  16,  4,  2,   45, 123, 12, 56, 33, 1,   4,   3,  4,   7,
        0,  0,   0,   3,  2,   0,  16,  4,  2,  4,  2,   0,   7,  4,   5,
        2,  26,  2,   25, 6,   11, 6,   1,  16, 2,  6,   17,  77, 15,  3,
        35, 0,   1,   0,  5,   1,  0,   38, 16, 6,  3,   12,  3,  3,   3,
        0,  9,   3,   1,  3,   5,  2,   9,  0,  18, 0,   25,  1,  3,   32,
        1,  72,  46,  6,  2,   7,  1,   3,  14, 17, 0,   28,  1,  40,  13,
        0,  20,  15,  40, 6,   38, 24,  12, 43, 1,  1,   9,   0,  12,  6,
        0,  6,   2,   4,  19,  3,  7,   1,  48, 0,  9,   5,   0,  5,   6,
        9,  6,   10,  15, 2,   11, 19,  3,  9,  2,  0,   1,   10, 1,   27,
        8,  1,   3,   6,  1,   14, 0,   26, 0,  27, 16,  3,   4,  9,   6,
        2,  23,  9,   10, 5,   25, 2,   1,  6,  1,  1,   48,  15, 9,   15,
        14, 3,   4,   26, 60,  29, 13,  37, 21, 1,  6,   4,   0,  2,   11,
        22, 23,  16,  16, 2,   2,  1,   3,  0,  5,  1,   6,   4,  0,   0,
        4,  0,   0,   8,  3,   0,  2,   5,  0,  7,  1,   7,   3,  13,  2,
        4,  10,  3,   0,  2,   31, 0,   18, 3,  0,  12,  10,  4,  1,   0,
        7,  5,   7,   0,  5,   4,  12,  2,  22, 10, 4,   2,   15, 2,   8,
        9,  0,   23,  2,  197, 51, 3,   1,  1,  4,  13,  4,   3,  21,  4,
        19, 3,   10,  5,  40,  0,  4,   1,  1,  10, 4,   1,   27, 34,  7,
        21, 2,   17,  2,  9,   6,  4,   2,  3,  0,  4,   2,   7,  8,   2,
        5,  1,   15,  21, 3,   4,  4,   2,  2,  17, 22,  1,   5,  22,  4,
        26, 7,   0,   32, 1,   11, 42,  15, 4,  1,  2,   5,   0,  19,  3,
        1,  8,   6,   0,  10,  1,  9,   2,  13, 30, 8,   2,   24, 17,  19,
        1,  4,   4,   25, 13,  0,  10,  16, 11, 39, 18,  8,   5,  30,  82,
        1,  6,   8,   18, 77,  11, 13,  20, 75, 11, 112, 78,  33, 3,   0,
        0,  60,  17,  84, 9,   1,  1,   12, 30, 10, 49,  5,   32, 158, 178,
        5,  5,   6,   3,  3,   1,  3,   1,  4,  7,  6,   19,  31, 21,  0,
        2,  9,   5,   6,  27,  4,  9,   8,  1,  76, 18,  12,  1,  4,   0,
        3,  3,   6,   3,  12,  2,  8,   30, 16, 2,  25,  1,   5,  5,   4,
        3,  0,   6,   10, 2,   3,  1,   0,  5,  1,  19,  3,   0,  8,   1,
        5,  2,   6,   0,  0,   0,  19,  1,  2,  0,  5,   1,   2,  5,   1,
        3,  7,   0,   4,  12,  7,  3,   10, 22, 0,  9,   5,   1,  0,   2,
        20, 1,   1,   3,  23,  30, 3,   9,  9,  1,  4,   191, 14, 3,   15,
        6,  8,   50,  0,  1,   0,  0,   4,  0,  0,  1,   0,   2,  4,   2,
        0,  2,   3,   0,  2,   0,  2,   2,  8,  7,  0,   1,   1,  1,   3,
        3,  17,  11,  91, 1,   9,  3,   2,  13, 4,  24,  15,  41, 3,   13,
        3,  1,   20,  4,  125, 29, 30,  1,  0,  4,  12,  2,   21, 4,   5,
        5,  19,  11,  0,  13,  11, 86,  2,  18, 0,  7,   1,   8,  8,   2,
        2,  22,  1,   2,  6,   5,  2,   0,  1,  2,  8,   0,   2,  0,   5,
        2,  1,   0,   2,  10,  2,  0,   5,  9,  2,  1,   2,   0,  1,   0,
        4,  0,   0,   10, 2,   5,  3,   0,  6,  1,  0,   1,   4,  4,   33,
        3,  13,  17,  3,  18,  6,  4,   7,  1,  5,  78,  0,   4,  1,   13,
        7,  1,   8,   1,  0,   35, 27,  15, 3,  0,  0,   0,   1,  11,  5,
        41, 38,  15,  22, 6,   14, 14,  2,  1,  11, 6,   20,  63, 5,   8,
        27, 7,   11,  2,  2,   40, 58,  23, 50, 54, 56,  293, 8,  8,   1,
        5,  1,   14,  0,  1,   12, 37,  89, 8,  8,  8,   2,   10, 6,   0,
        0,  0,   4,   5,  2,   1,  0,   1,  1,  2,  7,   0,   3,  3,   0,
        4,  6,   0,   3,  2,   19, 3,   8,  0,  0,  0,   4,   4,  16,  0,
        4,  1,   5,   1,  3,   0,  3,   4,  6,  2,  17,  10,  10, 31,  6,
        4,  3,   6,   10, 126, 7,  3,   2,  2,  0,  9,   0,   0,  5,   20,
        13, 0,   15,  0,  6,   0,  2,   5,  8,  64, 50,  3,   2,  12,  2,
        9,  0,   0,   11, 8,   20, 109, 2,  18, 23, 0,   0,   9,  61,  3,
        0,  28,  41,  77, 27,  19, 17,  81, 5,  2,  14,  5,   83, 57,  252,
        14, 154, 263, 14, 20,  8,  13,  6,  57, 39, 38,
    };
    static ImWchar base_ranges[] = {
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
        0x3000, 0x30FF, // Punctuations, Hiragana, Katakana
        0x31F0, 0x31FF, // Katakana Phonetic Extensions
        0xFF00, 0xFFEF, // Half-width characters
    };
    static bool    full_ranges_unpacked = false;
    static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) +
                               IM_ARRAYSIZE(offsets_from_0x4E00) * 2 + 1];
    if (!full_ranges_unpacked)
    {
        // Unpack
        int codepoint = 0x4e00;
        memcpy(full_ranges, base_ranges, sizeof(base_ranges));
        ImWchar* dst = full_ranges + IM_ARRAYSIZE(base_ranges);
        ;
        for (int n = 0; n < IM_ARRAYSIZE(offsets_from_0x4E00); n++, dst += 2)
            dst[0] = dst[1] =
                (ImWchar)(codepoint += (offsets_from_0x4E00[n] + 1));
        dst[0]               = 0;
        full_ranges_unpacked = true;
    }
    return &full_ranges[0];
}

const ImWchar* ImFontAtlas::GetGlyphRangesCyrillic()
{
    static const ImWchar ranges[] = {
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
        0x0400, 0x052F, // Cyrillic + Cyrillic Supplement
        0x2DE0, 0x2DFF, // Cyrillic Extended-A
        0xA640, 0xA69F, // Cyrillic Extended-B
        0,
    };
    return &ranges[0];
}

const ImWchar* ImFontAtlas::GetGlyphRangesThai()
{
    static const ImWchar ranges[] = {
        0x0020, 0x00FF, // Basic Latin
        0x2010, 0x205E, // Punctuations
        0x0E00, 0x0E7F, // Thai
        0,
    };
    return &ranges[0];
}

//-----------------------------------------------------------------------------
// ImFontAtlas::GlyphRangesBuilder
//-----------------------------------------------------------------------------

void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text,
                                              const char* text_end)
{
    while (text_end ? (text < text_end) : *text)
    {
        unsigned int c     = 0;
        int          c_len = ImTextCharFromUtf8(&c, text, text_end);
        text += c_len;
        if (c_len == 0)
            break;
        if (c < 0x10000)
            AddChar((ImWchar)c);
    }
}

void ImFontAtlas::GlyphRangesBuilder::AddRanges(const ImWchar* ranges)
{
    for (; ranges[0]; ranges += 2)
        for (ImWchar c = ranges[0]; c <= ranges[1]; c++)
            AddChar(c);
}

void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
{
    for (int n = 0; n < 0x10000; n++)
        if (GetBit(n))
        {
            out_ranges->push_back((ImWchar)n);
            while (n < 0x10000 && GetBit(n + 1))
                n++;
            out_ranges->push_back((ImWchar)n);
        }
    out_ranges->push_back(0);
}

//-----------------------------------------------------------------------------
// ImFont
//-----------------------------------------------------------------------------

ImFont::ImFont()
{
    Scale         = 1.0f;
    FallbackChar  = (ImWchar)'?';
    DisplayOffset = ImVec2(0.0f, 1.0f);
    ClearOutputData();
}

ImFont::~ImFont()
{
    // Invalidate active font so that the user gets a clear crash instead of a
    // dangling pointer. If you want to delete fonts you need to do it between
    // Render() and NewFrame().
    // FIXME-CLEANUP
    /*
    ImGuiContext& g = *GImGui;
    if (g.Font == this)
        g.Font = NULL;
    */
    ClearOutputData();
}

void ImFont::ClearOutputData()
{
    FontSize = 0.0f;
    Glyphs.clear();
    IndexAdvanceX.clear();
    IndexLookup.clear();
    FallbackGlyph    = NULL;
    FallbackAdvanceX = 0.0f;
    ConfigDataCount  = 0;
    ConfigData       = NULL;
    ContainerAtlas   = NULL;
    Ascent = Descent    = 0.0f;
    MetricsTotalSurface = 0;
}

void ImFont::BuildLookupTable()
{
    int max_codepoint = 0;
    for (int i = 0; i != Glyphs.Size; i++)
        max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);

    IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved
    IndexAdvanceX.clear();
    IndexLookup.clear();
    GrowIndex(max_codepoint + 1);
    for (int i = 0; i < Glyphs.Size; i++)
    {
        int codepoint            = (int)Glyphs[i].Codepoint;
        IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX;
        IndexLookup[codepoint]   = (unsigned short)i;
    }

    // Create a glyph to handle TAB
    // FIXME: Needs proper TAB handling but it needs to be contextualized (or we
    // could arbitrary say that each string starts at "column 0" ?)
    if (FindGlyph((unsigned short)' '))
    {
        if (Glyphs.back().Codepoint !=
            '\t') // So we can call this function multiple times
            Glyphs.resize(Glyphs.Size + 1);
        ImFontGlyph& tab_glyph = Glyphs.back();
        tab_glyph              = *FindGlyph((unsigned short)' ');
        tab_glyph.Codepoint    = '\t';
        tab_glyph.AdvanceX *= 4;
        IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX;
        IndexLookup[(int)tab_glyph.Codepoint] =
            (unsigned short)(Glyphs.Size - 1);
    }

    FallbackGlyph    = NULL;
    FallbackGlyph    = FindGlyph(FallbackChar);
    FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f;
    for (int i = 0; i < max_codepoint + 1; i++)
        if (IndexAdvanceX[i] < 0.0f)
            IndexAdvanceX[i] = FallbackAdvanceX;
}

void ImFont::SetFallbackChar(ImWchar c)
{
    FallbackChar = c;
    BuildLookupTable();
}

void ImFont::GrowIndex(int new_size)
{
    IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size);
    if (new_size <= IndexLookup.Size)
        return;
    IndexAdvanceX.resize(new_size, -1.0f);
    IndexLookup.resize(new_size, (unsigned short)-1);
}

void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1,
                      float u0, float v0, float u1, float v1, float advance_x)
{
    Glyphs.resize(Glyphs.Size + 1);
    ImFontGlyph& glyph = Glyphs.back();
    glyph.Codepoint    = (ImWchar)codepoint;
    glyph.X0           = x0;
    glyph.Y0           = y0;
    glyph.X1           = x1;
    glyph.Y1           = y1;
    glyph.U0           = u0;
    glyph.V0           = v0;
    glyph.U1           = u1;
    glyph.V1           = v1;
    glyph.AdvanceX =
        advance_x +
        ConfigData->GlyphExtraSpacing.x; // Bake spacing into AdvanceX

    if (ConfigData->PixelSnapH)
        glyph.AdvanceX = (float)(int)(glyph.AdvanceX + 0.5f);

    // Compute rough surface usage metrics (+1 to account for average padding,
    // +0.99 to round)
    MetricsTotalSurface +=
        (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + 1.99f) *
        (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + 1.99f);
}

void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)
{
    IM_ASSERT(
        IndexLookup.Size >
        0); // Currently this can only be called AFTER the font has been built,
            // aka after calling ImFontAtlas::GetTexDataAs*() function.
    int index_size = IndexLookup.Size;

    if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 &&
        !overwrite_dst) // 'dst' already exists
        return;
    if (src >= index_size &&
        dst >= index_size) // both 'dst' and 'src' don't exist -> no-op
        return;

    GrowIndex(dst + 1);
    IndexLookup[dst] =
        (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1;
    IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f;
}

const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
{
    if (c < IndexLookup.Size)
    {
        const unsigned short i = IndexLookup[c];
        if (i != (unsigned short)-1)
            return &Glyphs.Data[i];
    }
    return FallbackGlyph;
}

const char* ImFont::CalcWordWrapPositionA(float scale, const char* text,
                                          const char* text_end,
                                          float       wrap_width) const
{
    // Simple word-wrapping for English, not full-featured. Please submit
    // failing cases!
    // FIXME: Much possible improvements (don't cut things like "word !",
    // "word!!!" but cut within "word,,,,", more sensible support for
    // punctuations, support for Unicode punctuations, etc.)

    // For references, possible wrap point marked with ^
    //  "aaa bbb, ccc,ddd. eee   fff. ggg!"
    //      ^    ^    ^   ^   ^__    ^    ^

    // List of hardcoded separators: .,;!?'"

    // Skip extra blanks after a line returns (that includes not counting them
    // in width computation) e.g. "Hello    world" --> "Hello" "World"

    // Cut words that cannot possibly fit within one line.
    // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr"
    // "opical" "fish"

    float line_width  = 0.0f;
    float word_width  = 0.0f;
    float blank_width = 0.0f;
    wrap_width /=
        scale; // We work with unscaled widths to avoid scaling every characters

    const char* word_end      = text;
    const char* prev_word_end = NULL;
    bool        inside_word   = true;

    const char* s = text;
    while (s < text_end)
    {
        unsigned int c = (unsigned int)*s;
        const char*  next_s;
        if (c < 0x80)
            next_s = s + 1;
        else
            next_s = s + ImTextCharFromUtf8(&c, s, text_end);
        if (c == 0)
            break;

        if (c < 32)
        {
            if (c == '\n')
            {
                line_width = word_width = blank_width = 0.0f;
                inside_word                           = true;
                s                                     = next_s;
                continue;
            }
            if (c == '\r')
            {
                s = next_s;
                continue;
            }
        }

        const float char_width =
            ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c]
                                         : FallbackAdvanceX);
        if (ImCharIsSpace(c))
        {
            if (inside_word)
            {
                line_width += blank_width;
                blank_width = 0.0f;
                word_end    = s;
            }
            blank_width += char_width;
            inside_word = false;
        }
        else
        {
            word_width += char_width;
            if (inside_word)
            {
                word_end = next_s;
            }
            else
            {
                prev_word_end = word_end;
                line_width += word_width + blank_width;
                word_width = blank_width = 0.0f;
            }

            // Allow wrapping after punctuation.
            inside_word = !(c == '.' || c == ',' || c == ';' || c == '!' ||
                            c == '?' || c == '\"');
        }

        // We ignore blank width at the end of the line (they can be skipped)
        if (line_width + word_width >= wrap_width)
        {
            // Words that cannot possibly fit within an entire line will be cut
            // anywhere.
            if (word_width < wrap_width)
                s = prev_word_end ? prev_word_end : word_end;
            break;
        }

        s = next_s;
    }

    return s;
}

ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width,
                             const char* text_begin, const char* text_end,
                             const char** remaining) const
{
    if (!text_end)
        text_end =
            text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this.

    const float line_height = size;
    const float scale       = size / FontSize;

    ImVec2 text_size  = ImVec2(0, 0);
    float  line_width = 0.0f;

    const bool  word_wrap_enabled = (wrap_width > 0.0f);
    const char* word_wrap_eol     = NULL;

    const char* s = text_begin;
    while (s < text_end)
    {
        if (word_wrap_enabled)
        {
            // Calculate how far we can render. Requires two passes on the
            // string data but keeps the code simple and not intrusive for
            // what's essentially an uncommon feature.
            if (!word_wrap_eol)
            {
                word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end,
                                                      wrap_width - line_width);
                if (word_wrap_eol ==
                    s) // Wrap_width is too small to fit anything. Force
                       // displaying 1 character to minimize the height
                       // discontinuity.
                    word_wrap_eol++; // +1 may not be a character start point in
                                     // UTF-8 but it's ok because we use s >=
                                     // word_wrap_eol below
            }

            if (s >= word_wrap_eol)
            {
                if (text_size.x < line_width)
                    text_size.x = line_width;
                text_size.y += line_height;
                line_width    = 0.0f;
                word_wrap_eol = NULL;

                // Wrapping skips upcoming blanks
                while (s < text_end)
                {
                    const char c = *s;
                    if (ImCharIsSpace(c))
                    {
                        s++;
                    }
                    else if (c == '\n')
                    {
                        s++;
                        break;
                    }
                    else
                    {
                        break;
                    }
                }
                continue;
            }
        }

        // Decode and advance source
        const char*  prev_s = s;
        unsigned int c      = (unsigned int)*s;
        if (c < 0x80)
        {
            s += 1;
        }
        else
        {
            s += ImTextCharFromUtf8(&c, s, text_end);
            if (c == 0) // Malformed UTF-8?
                break;
        }

        if (c < 32)
        {
            if (c == '\n')
            {
                text_size.x = ImMax(text_size.x, line_width);
                text_size.y += line_height;
                line_width = 0.0f;
                continue;
            }
            if (c == '\r')
                continue;
        }

        const float char_width =
            ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c]
                                         : FallbackAdvanceX) *
            scale;
        if (line_width + char_width >= max_width)
        {
            s = prev_s;
            break;
        }

        line_width += char_width;
    }

    if (text_size.x < line_width)
        text_size.x = line_width;

    if (line_width > 0 || text_size.y == 0.0f)
        text_size.y += line_height;

    if (remaining)
        *remaining = s;

    return text_size;
}

void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos,
                        ImU32 col, unsigned short c) const
{
    if (c == ' ' || c == '\t' || c == '\n' ||
        c == '\r') // Match behavior of RenderText(), those 4 codepoints are
                   // hard-coded.
        return;
    if (const ImFontGlyph* glyph = FindGlyph(c))
    {
        float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
        pos.x       = (float)(int)pos.x + DisplayOffset.x;
        pos.y       = (float)(int)pos.y + DisplayOffset.y;
        draw_list->PrimReserve(6, 4);
        draw_list->PrimRectUV(
            ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale),
            ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale),
            ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);
    }
}

void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos,
                        ImU32 col, const ImVec4& clip_rect,
                        const char* text_begin, const char* text_end,
                        float wrap_width, bool cpu_fine_clip) const
{
    if (!text_end)
        text_end = text_begin +
                   strlen(text_begin); // ImGui functions generally already
                                       // provides a valid text_end, so this is
                                       // merely to handle direct calls.

    // Align to be pixel perfect
    pos.x   = (float)(int)pos.x + DisplayOffset.x;
    pos.y   = (float)(int)pos.y + DisplayOffset.y;
    float x = pos.x;
    float y = pos.y;
    if (y > clip_rect.w)
        return;

    const float scale             = size / FontSize;
    const float line_height       = FontSize * scale;
    const bool  word_wrap_enabled = (wrap_width > 0.0f);
    const char* word_wrap_eol     = NULL;

    // Skip non-visible lines
    const char* s = text_begin;
    if (!word_wrap_enabled && y + line_height < clip_rect.y)
        while (s < text_end && *s != '\n') // Fast-forward to next line
            s++;

    // Reserve vertices for remaining worse case (over-reserving is useful and
    // easily amortized)
    const int vtx_count_max     = (int)(text_end - s) * 4;
    const int idx_count_max     = (int)(text_end - s) * 6;
    const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;
    draw_list->PrimReserve(idx_count_max, vtx_count_max);

    ImDrawVert*  vtx_write       = draw_list->_VtxWritePtr;
    ImDrawIdx*   idx_write       = draw_list->_IdxWritePtr;
    unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;

    while (s < text_end)
    {
        if (word_wrap_enabled)
        {
            // Calculate how far we can render. Requires two passes on the
            // string data but keeps the code simple and not intrusive for
            // what's essentially an uncommon feature.
            if (!word_wrap_eol)
            {
                word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end,
                                                      wrap_width - (x - pos.x));
                if (word_wrap_eol ==
                    s) // Wrap_width is too small to fit anything. Force
                       // displaying 1 character to minimize the height
                       // discontinuity.
                    word_wrap_eol++; // +1 may not be a character start point in
                                     // UTF-8 but it's ok because we use s >=
                                     // word_wrap_eol below
            }

            if (s >= word_wrap_eol)
            {
                x = pos.x;
                y += line_height;
                word_wrap_eol = NULL;

                // Wrapping skips upcoming blanks
                while (s < text_end)
                {
                    const char c = *s;
                    if (ImCharIsSpace(c))
                    {
                        s++;
                    }
                    else if (c == '\n')
                    {
                        s++;
                        break;
                    }
                    else
                    {
                        break;
                    }
                }
                continue;
            }
        }

        // Decode and advance source
        unsigned int c = (unsigned int)*s;
        if (c < 0x80)
        {
            s += 1;
        }
        else
        {
            s += ImTextCharFromUtf8(&c, s, text_end);
            if (c == 0) // Malformed UTF-8?
                break;
        }

        if (c < 32)
        {
            if (c == '\n')
            {
                x = pos.x;
                y += line_height;

                if (y > clip_rect.w)
                    break;
                if (!word_wrap_enabled && y + line_height < clip_rect.y)
                    while (s < text_end &&
                           *s != '\n') // Fast-forward to next line
                        s++;
                continue;
            }
            if (c == '\r')
                continue;
        }

        float char_width = 0.0f;
        if (const ImFontGlyph* glyph = FindGlyph((unsigned short)c))
        {
            char_width = glyph->AdvanceX * scale;

            // Arbitrarily assume that both space and tabs are empty glyphs as
            // an optimization
            if (c != ' ' && c != '\t')
            {
                // We don't do a second finer clipping test on the Y axis as
                // we've already skipped anything before clip_rect.y and exit
                // once we pass clip_rect.w
                float x1 = x + glyph->X0 * scale;
                float x2 = x + glyph->X1 * scale;
                float y1 = y + glyph->Y0 * scale;
                float y2 = y + glyph->Y1 * scale;
                if (x1 <= clip_rect.z && x2 >= clip_rect.x)
                {
                    // Render a character
                    float u1 = glyph->U0;
                    float v1 = glyph->V0;
                    float u2 = glyph->U1;
                    float v2 = glyph->V1;

                    // CPU side clipping used to fit text in their frame when
                    // the frame is too small. Only does clipping for axis
                    // aligned quads.
                    if (cpu_fine_clip)
                    {
                        if (x1 < clip_rect.x)
                        {
                            u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) *
                                          (u2 - u1);
                            x1 = clip_rect.x;
                        }
                        if (y1 < clip_rect.y)
                        {
                            v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) *
                                          (v2 - v1);
                            y1 = clip_rect.y;
                        }
                        if (x2 > clip_rect.z)
                        {
                            u2 = u1 +
                                 ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);
                            x2 = clip_rect.z;
                        }
                        if (y2 > clip_rect.w)
                        {
                            v2 = v1 +
                                 ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);
                            y2 = clip_rect.w;
                        }
                        if (y1 >= y2)
                        {
                            x += char_width;
                            continue;
                        }
                    }

                    // We are NOT calling PrimRectUV() here because non-inlined
                    // causes too much overhead in a debug builds. Inlined here:
                    {
                        idx_write[0]       = (ImDrawIdx)(vtx_current_idx);
                        idx_write[1]       = (ImDrawIdx)(vtx_current_idx + 1);
                        idx_write[2]       = (ImDrawIdx)(vtx_current_idx + 2);
                        idx_write[3]       = (ImDrawIdx)(vtx_current_idx);
                        idx_write[4]       = (ImDrawIdx)(vtx_current_idx + 2);
                        idx_write[5]       = (ImDrawIdx)(vtx_current_idx + 3);
                        vtx_write[0].pos.x = x1;
                        vtx_write[0].pos.y = y1;
                        vtx_write[0].col   = col;
                        vtx_write[0].uv.x  = u1;
                        vtx_write[0].uv.y  = v1;
                        vtx_write[1].pos.x = x2;
                        vtx_write[1].pos.y = y1;
                        vtx_write[1].col   = col;
                        vtx_write[1].uv.x  = u2;
                        vtx_write[1].uv.y  = v1;
                        vtx_write[2].pos.x = x2;
                        vtx_write[2].pos.y = y2;
                        vtx_write[2].col   = col;
                        vtx_write[2].uv.x  = u2;
                        vtx_write[2].uv.y  = v2;
                        vtx_write[3].pos.x = x1;
                        vtx_write[3].pos.y = y2;
                        vtx_write[3].col   = col;
                        vtx_write[3].uv.x  = u1;
                        vtx_write[3].uv.y  = v2;
                        vtx_write += 4;
                        vtx_current_idx += 4;
                        idx_write += 6;
                    }
                }
            }
        }

        x += char_width;
    }

    // Give back unused vertices
    draw_list->VtxBuffer.resize((int)(vtx_write - draw_list->VtxBuffer.Data));
    draw_list->IdxBuffer.resize((int)(idx_write - draw_list->IdxBuffer.Data));
    draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -=
        (idx_expected_size - draw_list->IdxBuffer.Size);
    draw_list->_VtxWritePtr   = vtx_write;
    draw_list->_IdxWritePtr   = idx_write;
    draw_list->_VtxCurrentIdx = (unsigned int)draw_list->VtxBuffer.Size;
}

//-----------------------------------------------------------------------------
// Internals Drawing Helpers
//-----------------------------------------------------------------------------

static inline float ImAcos01(float x)
{
    if (x <= 0.0f)
        return IM_PI * 0.5f;
    if (x >= 1.0f)
        return 0.0f;
    return acosf(x);
    // return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x
    // + 1.5707963267948966f; // Cheap approximation, may be enough for what we
    // do.
}

// FIXME: Cleanup and move code to ImDrawList.
void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect,
                                   ImU32 col, float x_start_norm,
                                   float x_end_norm, float rounding)
{
    if (x_end_norm == x_start_norm)
        return;
    if (x_start_norm > x_end_norm)
        ImSwap(x_start_norm, x_end_norm);

    ImVec2 p0 =
        ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y);
    ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y);
    if (rounding == 0.0f)
    {
        draw_list->AddRectFilled(p0, p1, col, 0.0f);
        return;
    }

    rounding                 = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f,
                             (rect.Max.y - rect.Min.y) * 0.5f) -
                           1.0f,
                       0.0f, rounding);
    const float inv_rounding = 1.0f / rounding;
    const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding);
    const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding);
    const float x0     = ImMax(p0.x, rect.Min.x + rounding);
    if (arc0_b == arc0_e)
    {
        draw_list->PathLineTo(ImVec2(x0, p1.y));
        draw_list->PathLineTo(ImVec2(x0, p0.y));
    }
    else if (arc0_b == 0.0f && arc0_e == IM_PI * 0.5f)
    {
        draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3,
                                 6); // BL
        draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6,
                                 9); // TR
    }
    else
    {
        draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding,
                             IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL
        draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding,
                             IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR
    }
    if (p1.x > rect.Min.x + rounding)
    {
        const float arc1_b =
            ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding);
        const float arc1_e =
            ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding);
        const float x1 = ImMin(p1.x, rect.Max.x - rounding);
        if (arc1_b == arc1_e)
        {
            draw_list->PathLineTo(ImVec2(x1, p0.y));
            draw_list->PathLineTo(ImVec2(x1, p1.y));
        }
        else if (arc1_b == 0.0f && arc1_e == IM_PI * 0.5f)
        {
            draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9,
                                     12); // TR
            draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0,
                                     3); // BR
        }
        else
        {
            draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e,
                                 -arc1_b, 3); // TR
            draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b,
                                 +arc1_e, 3); // BR
        }
    }
    draw_list->PathFillConvex(col);
}

//-----------------------------------------------------------------------------
// DEFAULT FONT DATA
//-----------------------------------------------------------------------------
// Compressed with stb_compress() then converted to a C array.
// Use the program in extra_fonts/binary_to_compressed_c.cpp to create the array
// from a TTF file. Decompression from stb.h (public domain) by Sean Barrett
// https://github.com/nothings/stb/blob/master/stb.h
//-----------------------------------------------------------------------------

static unsigned int stb_decompress_length(unsigned char* input)
{
    return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11];
}

static unsigned char *stb__barrier, *stb__barrier2, *stb__barrier3,
    *stb__barrier4;
static unsigned char* stb__dout;
static void           stb__match(unsigned char* data, unsigned int length)
{
    // INVERSE of memmove... write each byte before copying the next...
    IM_ASSERT(stb__dout + length <= stb__barrier);
    if (stb__dout + length > stb__barrier)
    {
        stb__dout += length;
        return;
    }
    if (data < stb__barrier4)
    {
        stb__dout = stb__barrier + 1;
        return;
    }
    while (length--)
        *stb__dout++ = *data++;
}

static void stb__lit(unsigned char* data, unsigned int length)
{
    IM_ASSERT(stb__dout + length <= stb__barrier);
    if (stb__dout + length > stb__barrier)
    {
        stb__dout += length;
        return;
    }
    if (data < stb__barrier2)
    {
        stb__dout = stb__barrier + 1;
        return;
    }
    memcpy(stb__dout, data, length);
    stb__dout += length;
}

#define stb__in2(x) ((i[x] << 8) + i[(x) + 1])
#define stb__in3(x) ((i[x] << 16) + stb__in2((x) + 1))
#define stb__in4(x) ((i[x] << 24) + stb__in3((x) + 1))

static unsigned char* stb_decompress_token(unsigned char* i)
{
    if (*i >= 0x20)
    { // use fewer if's for cases that expand small
        if (*i >= 0x80)
            stb__match(stb__dout - i[1] - 1, i[0] - 0x80 + 1), i += 2;
        else if (*i >= 0x40)
            stb__match(stb__dout - (stb__in2(0) - 0x4000 + 1), i[2] + 1),
                i += 3;
        else /* *i >= 0x20 */
            stb__lit(i + 1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1);
    }
    else
    { // more ifs for cases that expand large, since overhead is amortized
        if (*i >= 0x18)
            stb__match(stb__dout - (stb__in3(0) - 0x180000 + 1), i[3] + 1),
                i += 4;
        else if (*i >= 0x10)
            stb__match(stb__dout - (stb__in3(0) - 0x100000 + 1),
                       stb__in2(3) + 1),
                i += 5;
        else if (*i >= 0x08)
            stb__lit(i + 2, stb__in2(0) - 0x0800 + 1),
                i += 2 + (stb__in2(0) - 0x0800 + 1);
        else if (*i == 0x07)
            stb__lit(i + 3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1);
        else if (*i == 0x06)
            stb__match(stb__dout - (stb__in3(1) + 1), i[4] + 1), i += 5;
        else if (*i == 0x04)
            stb__match(stb__dout - (stb__in3(1) + 1), stb__in2(4) + 1), i += 6;
    }
    return i;
}

static unsigned int stb_adler32(unsigned int adler32, unsigned char* buffer,
                                unsigned int buflen)
{
    const unsigned long ADLER_MOD = 65521;
    unsigned long       s1 = adler32 & 0xffff, s2 = adler32 >> 16;
    unsigned long       blocklen, i;

    blocklen = buflen % 5552;
    while (buflen)
    {
        for (i = 0; i + 7 < blocklen; i += 8)
        {
            s1 += buffer[0], s2 += s1;
            s1 += buffer[1], s2 += s1;
            s1 += buffer[2], s2 += s1;
            s1 += buffer[3], s2 += s1;
            s1 += buffer[4], s2 += s1;
            s1 += buffer[5], s2 += s1;
            s1 += buffer[6], s2 += s1;
            s1 += buffer[7], s2 += s1;

            buffer += 8;
        }

        for (; i < blocklen; ++i)
            s1 += *buffer++, s2 += s1;

        s1 %= ADLER_MOD, s2 %= ADLER_MOD;
        buflen -= blocklen;
        blocklen = 5552;
    }
    return (unsigned int)(s2 << 16) + (unsigned int)s1;
}

static unsigned int stb_decompress(unsigned char* output, unsigned char* i,
                                   unsigned int length)
{
    unsigned int olen;
    if (stb__in4(0) != 0x57bC0000)
        return 0;
    if (stb__in4(4) != 0)
        return 0; // error! stream is > 4GB
    olen          = stb_decompress_length(i);
    stb__barrier2 = i;
    stb__barrier3 = i + length;
    stb__barrier  = output + olen;
    stb__barrier4 = output;
    i += 16;

    stb__dout = output;
    for (;;)
    {
        unsigned char* old_i = i;
        i                    = stb_decompress_token(i);
        if (i == old_i)
        {
            if (*i == 0x05 && i[1] == 0xfa)
            {
                IM_ASSERT(stb__dout == output + olen);
                if (stb__dout != output + olen)
                    return 0;
                if (stb_adler32(1, output, olen) != (unsigned int)stb__in4(2))
                    return 0;
                return olen;
            }
            else
            {
                IM_ASSERT(0); /* NOTREACHED */
                return 0;
            }
        }
        IM_ASSERT(stb__dout <= output + olen);
        if (stb__dout > output + olen)
            return 0;
    }
}

//-----------------------------------------------------------------------------
// ProggyClean.ttf
// Copyright (c) 2004, 2005 Tristan Grimmer
// MIT license (see License.txt in
// http://www.upperbounds.net/download/ProggyClean.ttf.zip) Download and more
// information at http://upperbounds.net
//-----------------------------------------------------------------------------
// File: 'ProggyClean.ttf' (41208 bytes)
// Exported using binary_to_compressed_c.cpp
//-----------------------------------------------------------------------------
static const char proggy_clean_ttf_compressed_data_base85[11980 + 1] =
    "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/"
    "e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';"
    "9Crc6tgXmKVeU2cD4Eo3R/"
    "2*>]b(MC;$jPfY.;h^`IWM9<Lh2TlS+f-s$o6Q<BWH`YiU.xfLq$N;$0iR/GX:U(jcW2p/"
    "W*q?-qmnUCI;jHSAiFWM.R*kU@C=GH?a9wp8f$e.-4^Qg1)Q-GL(lf(r/7GrRgwV%MS=C#"
    "`8ND>Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1<q-UE31#^-V'8IRUo7Qf./"
    "L>=Ke$$'5F%)]0^#0X@U.a<r:QLtFsLcL6##lOj)#.Y5<-R&KgLwqJfLgN&;Q?gI^#DY2uL"
    "i@^rMl9t=cWq6##weg>$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//"
    "L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;-<nLENhvx>-VsM.M0rJfLH2eTM`*"
    "oJMHRC`N"
    "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^"
    "GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`&#0j@'DbG&#^$PG.Ll+DNa<XCMKEV*N)LN/N"
    "*b=%Q6pia-Xg8I$<MR&,VdJe$<(7G;Ckl'&hF;;$<_=X(b.RS%%)###MPBuuE1V:v&cX&#2m#("
    "&cV]`k9OhLMbn%s$G2,B$BfD3X*sp5#l,$R#]x_X1xKX%b5U*[r5iMfUo9U`N99hG)"
    "tm+/Us9pG)XPu`<0s-)WTt(gCRxIg(%6sfh=ktMKn3j)<6<b5Sk_/0(^]AaN#(p/"
    "L>&VZ>1i%h1S9u5o@YaaW$e+b<TWFn/"
    "Z:Oh(Cx2$lNEoN^e)#CFY@@I;BOQ*sRwZtZxRcU7uW6CX"
    "ow0i(?$Q[cjOd[P4d)]>ROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%"
    "lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc."
    "x]Ip.PH^'/aqUO/$1WxLoW0[iLA<QT;5HKD+@qQ'NQ(3_PLhE48R.qAPSwQ0/"
    "WK?Z,[x?-J;jQTWA0X@KJ(_Y8N-:/M74:/"
    "-ZpKrUss?d#dZq]DAbkU*JqkL+nwX@@47`5>w=4h(9.`G"
    "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/"
    "EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?G<Nald$qs]@]L<J7bR*>gv:[7MI2k).'2($"
    "5FNP&EQ(,)"
    "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]"
    "O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#"
    "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/"
    "4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$"
    "x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM"
    "_3YlQC7(p7q)&](`6_c)$/"
    "*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0<q-]L_?^)1vw'.,MRsqVr.L;aN&#/"
    "EgJ)PBc[-f>+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu"
    "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+"
    "Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/"
    "/oL?#h7gl85[qW/"
    "NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&"
    "O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[K<L"
    "%a2E-grWVM3@2=-k22tL]4$##6We'8UJCKE[d_=%wI;'6X-GsLX4j^SgJ$##R*w,vP3wK#iiW&"
    "#*h^D&R?jp7+/u&#(AP##XU8c$fSYW-J95_-Dp[g9wcO&#M-h1OcJlc-*vpw0xUX&#"
    "OQFKNX@QI'IoPp7nb,QU//"
    "MQ&ZDkKP)X<WSVL(68uVl&#c'[0#(s1X&xm$Y%B7*K:eDA323j998GXbA#pwMs-jgD$9QISB-"
    "A_(aN4xoFM^@C58D0+Q+q3n0#3U1InDjF682-SjMXJK)("
    "h$hxua_K]ul92%'BOU&#BRRh-slg8KDlr:%L71Ka:.A;%YULjDPmL<LYs8i#XwJOYaKPKc1h:'"
    "9Ke,g)b),78=I39B;xiY$bgGw-&.Zi9InXDuYa%G*f2Bq7mn9^#p1vv%#(Wi-;/Z5h"
    "o;#2:;%d&#x9v68C5g?ntX0X)pT`;%pB3q7mgGN)3%(P8nTd5L7GeA-GL@+%J3u2:(Yf>et`e;"
    ")f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO"
    "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$["
    "guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J<j$UpK<Q4a1]MupW^-"
    "sj_$%[HK%'F####QRZJ::Y3EGl4'@%FkiAOg#p[##O`gukTfBHagL<LHw%q&OV0##F=6/"
    ":chIm0@eCP8X]:kFI%hl8hgO@RcBhS-@Qb$%+m=hPDLg*%K8ln(wcf3/'DW-$.lR?n[nCH-"
    "eXOONTJlh:.RYF%3'p6sq:UIMA945&^HFS87@$EP2iG<-lCO$%c`uKGD3rC$x0BL8aFn--`ke%"
    "#HMP'vh1/R&O_J9'um,.<tx[@%wsJk&bUT2`0uMv7gg#qp/ij.L56'hl;.s5CUrxjO"
    "M7-##.l+Au'A&O:-T72L]P`&=;ctp'XScX*rU.>-XTt,%OVU4)S1+R-#dg0/"
    "Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/"
    "6(^xtk%"
    "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$M<Jnq79VsJW/mWS*PUiq76;]/"
    "NM_>hLbxfc$mj`,O;&%W2m`Zh:/"
    ")Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]"
    "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,"
    "UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et"
    "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$<M-SGZ':+Q_k+uvOSLiEo(<aD/"
    "K<CCc`'Lx>'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:"
    "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#"
    "FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VB<HFF*qL("
    "$/V,;(kXZejWO`<[5?\?ewY(*9=%wDc;,u<'9t3W-(H1th3+G]ucQ]kLs7df($/"
    "*JL]@*t7Bu_G3_7mp7<iaQjO@.kLg;x3B0lqp7Hf,^Ze7-##@/"
    "c58Mo(3;knp0%)A7?-W+eI'o8)b<"
    "nKnw'Ho8C=Y>pqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^"
    "V8O(x<<aG/1N$#FX$0V5Y6x'aErI3I$7x%E`v<-BY,)%-?Psf*l?%C3.mM(=/M0:JxG'?"
    "7WhH%o'a<-80g0NBxoO(GH<dM]n.+%q@jH?f.UsJ2Ggs&4<-e47&Kl+f//"
    "9@`b+?.TeN_&B8Ss?v;^Trk;f#YvJkl&w$]>-+k?'(<S:68tq*WoDfZu';mM?8X[ma8W%*`-=;"
    "D.(nc7/;"
    ")g:T1=^J$&BRV(-lTmNB6xqB[@0*o.erM*<SWF]u2=st-*(6v>^](H.aREZSi,#1:[IXaZFOm<"
    "-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M"
    "D?@f&1'BW-)Ju<L25gl8uhVm1hL$##*8###'A3/"
    "LkKW+(^rWX?5W_8g)a(m&K8P>#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/"
    "5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX("
    "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/"
    "V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO("
    "G0Sq7MVjJs"
    "bIu)'Z,*[>br5fX^:FPAWr-m2KgL<LUN098kTF&#lvo58=/vjDo;.;)Ka*hLR#/"
    "k=rKbxuV`>Q_nN6'8uTG&#1T5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@"
    "Tm&Q"
    "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//"
    ",]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aeg<Z'<$#4H)6,>e0jT6'N#(q%."
    "O=?2S]u*(m<-"
    "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/"
    "9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i"
    "sZ88+dKQ)W6>J%CL<KE>`.d*(B`-n8D9oK<Up]c$X$(,)M8Zt7/"
    "[rdkqTgl-0cuGMv'?>-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P&#9r+$%CE="
    "68>K8r0=dSC%%(@p7"
    ".m7jilQ02'0-VWAg<a/''3u.=4L$Y)6k/K:_[3=&jvL<L0C/"
    "2'v:^;-DIBW,B4E68:kZ;%?8(Q8BH=kO65BW?xSG&#@uU,DS*,?.+(o(#1vCS8#CHF>TlGW'b)"
    "Tq7VT9q^*^$$.:&N@@"
    "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/"
    "(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/"
    "m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*"
    "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/"
    ",;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?"
    "XJN4Nm&+YF]u"
    "@-W$U%VEQ/,,>>#)D<h#`)h0:<Q6909ua+&VU%n2:cG3FJ-%@Bj-DgLr`Hw&HAKjKjseK</"
    "xKT*)B,N9X3]krc12t'pgTV(Lv-tL[xg_%=M_q7a^x?7Ubd>#%8cY#YZ?=,`Wdxu/ae&#"
    "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$s<Eh#c&)q.MXI%#"
    "v9ROa5FZO%sF7q7Nwb&#ptUJ:aqJe$Sl68%.D###EC><?-aF&#RNQv>o8lKN%5/"
    "$(vdfq7+ebA#"
    "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(<c`Q8N)"
    "jEIF*+?P2a8g%)$q]o2aH8C&<SibC/q,(e:v;-b#6[$NtDZ84Je2KNvB#$P5?tQ3nt(0"
    "d=j.LQf./"
    "Ll33+(;q3L-w=8dX$#WF&uIJ@-bfI>%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-"
    "r;BoF&#4DoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8"
    "6e%B/:=>)N4xeW.*wft-;$'58-ESqr<b?UI(_%@[P46>#U`'6AQ]m&6/"
    "`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*"
    "BM9dM*hJMAo*c&#"
    "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM."
    "rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjL<Lni;''X.`$#8+1GD"
    ":k$YUWsbn8ogh6rxZ2Z9]%nd+>V#*8U_72Lh+2Q8Cj0i:6hp&$C/"
    ":p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#<NEdtg(n'=S1A(Q1/"
    "I&4([%dM`,Iu'1:_hL>SfD07&6D<fp8dHM7/g+"
    "tlPN9J*rKaPct&?'uBCem^jn%9_K)<,C5K3s=5g&GmJb*[SYq7K;TRLGCsM-$$;S%:Y@"
    "r7AK0pprpL<Lrh,q7e/%KWK:50I^+m'vi`3?%Zp+<-d+$L-Sv:@.o19n$s0&39;kn;S%BSq*"
    "$3WoJSCLweV[aZ'MQIjO<7;X-X;&+dMLvu#^UsGEC9WEc[X(wI7#2.(F0jV*eZf<-Qv3J-c+"
    "J5AlrB#$p(H68LvEA'q3n0#m,[`*8Ft)FcYgEud]CWfm68,(aLA$@EFTgLXoBq/UPlp7"
    ":d[/"
    ";r_ix=:TF`S5H-b<LI&HY(K=h#)]Lk$K14lVfm:x$H<3^Ql<M`$OhapBnkup'D#L$Pb_`N*g]"
    "2e;X/Dtg,bsj&K#2[-:iYr'_wgH)NUIR8a1n#S?Yej'h8^58UbZd+^FKD*T@;6A"
    "7aQC[K8d-(v6GI$x:T<&'Gp5Uf>@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K="
    "H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-<aN((^7('#Z0wK#5GX@7"
    "u][`*S^43933A4rl][`*O4CgLEl]v$1Q3AeF37dbXk,.)vj#x'd`;qgbQR%FW,2(?LO=s%"
    "Sc68%NP'##Aotl8x=BE#j1UD([3$M(]UI2LX3RpKN@;/#f'f/&_mt&F)XdF<9t4)Qa.*kT"
    "LwQ'(TTB9.xH'>#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/"
    "Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5<N?)NBS)QN*_I,?&)2'"
    "IM%L3I)X((e/dl2&8'<M"
    ":^#M*Q+[T.Xri.LYS3v%fF`68h;b-X[/En'CR.q7E)p'/"
    "kle2HM,u;^%OKC-N+Ll%F9CF<Nf'^#t2L,;27W:0O@6##U6W7:$rJfLWHj$#)woqBefIZ.PK<"
    "b*t7ed;p*_m;4ExK#h@&]>"
    "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,"
    ";s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%"
    "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<"
    "-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;"
    "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmL<"
    "LD)F^%[tC'8;+9E#C$g%#5Y>q9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:"
    "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/"
    "FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3<n-&%H%b<FDj2M<hH=&Eh<"
    "2Len$b*aTX=-8QxN)k11IM1c^j%"
    "9s<L<NFSo)B?+<-(GxsF,^-Eh@$4dXhN$+#rxK8'je'D7k`e;)2pYwPA'_p9&@^18ml1^[@"
    "g4t*[JOa*[=Qp7(qJ_oOL^('7fB&Hq-:sf,sNj8xq^>$U4O]GKx'm9)b@p7YsvK3w^YR-"
    "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/"
    "6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*"
    "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdF<TddF<9Ah-6&9tWoDlh]&1SpGMq>Ti1O*H&"
    "#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IX<N+T+0MlMBPQ*Vj>SsD<U4JHY"
    "8kD2)2fU/M#$e.)T4,_=8hLim[&);?UkK'-x?'(:siIfL<$pFM`i<?%W(mGDHM%>iWP,##P`%/"
    "L<eXi:@Z9C.7o=@(pXdAO/NLQ8lPl+HPOQa8wD8=^GlPa8TKI1CjhsCTSLJM'/Wl>-"
    "S(qw%sf/@%#B6;/"
    "U7K]uZbi^Oc^2n<bhPmUkMw>%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci."
    "(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL<LoNs'6,'85`"
    "0?t/'_U59@]ddF<#LdF<eWdF<OuN/45rY<-L@&#+fm>69=Lb,OcZV/"
    ");TTm8VI;?%OtJ<(b4mq7M6:u?KRdF<gR@2L=FNU-<b[(9c/"
    "ML3m;Z[$oF3g)GAWqpARc=<ROu7cL5l;-[A]%/"
    "+fsd;l#SafT/"
    "f*W]0=O'$(Tb<[)*@e775R-:Yob%g*>l*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'"
    "fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj"
    "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/"
    "LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#<IGe;__.thjZl<%w(Wk2xmp4Q@I#I9,DF]"
    "u7-P=.-_:YJ]aS@V"
    "?6*C()dOp7:WL,b&3Rg/"
    ".cmM9&r^>$(>.Z-I&J(Q0Hd5Q%7Co-b`-c<N(6r@ip+AurK<m86QIth*#v;-OBqi+L7wDE-"
    "Ir8K['m+DDSLwK&/.?-V%U_%3:qKNu$_b*B-kp7NaD'QdWQPK"
    "Yq[@>P)hI;*_F]u`Rb[.j8_Q/"
    "<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8<FfNkgg^oIbah*#8/"
    "Qt$F&:K*-(N/'+1vMB,u()-a.VUU*#[e%gAAO(S>WlA2);Sa"
    ">gXm8YB`1d@K#n]76-a$U,mF<fX]idqd)<3,]J7JmW4`6]uks=4-72L(jEk+:bJ0M^q-8Dm_Z?"
    "0olP1C9Sa&H[d&c$ooQUj]Exd*3ZM@-WGW2%s',B-_M%>%Ul:#/'xoFM9QX-$.QN'>"
    "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B</"
    "R90;eZ]%Ncq;-Tl]#F>2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'"
    "QG:`@I"
    "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/"
    "6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1<Vc52=u`3^o-n1'g4v58Hj&6_t7$#"
    "#?M)c<$bgQ_'SY((-xkA#"
    "Y(,p'H9rIVY-b,'%bCPF7.J<Up^,(dU1VY*5#WkTU>h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*"
    "7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-u<Hp,3@e^9UB1J+ak9-TN/mhKPg+AJYd$"
    "MlvAF_jCK*.O-^(63adMT->W%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;"
    "&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)"
    "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/"
    ",SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo"
    "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS["
    "0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P"
    "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*<h`e-GI7)?OK2A.d7_c)?wQ5AS@DL3r#7fSkgl6-++D:"
    "'A,uq7SvlB$pcpH'q3n0#_%dY#xCpr-l<F0NR@-##FEV6NTF6##$l84N1w?AO>'IAO"
    "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,["
    "R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#"
    ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/"
    "+4T<XoIB&hx=T1PcDaB&;HH+-AFr?(m9HZV)FKS8JCw;SD=6[^/DZUL`EUDf]GGlG&>"
    "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/"
    "X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#<xU?#@.i?#D:%@#HF7@#LRI@#P_[@#Tkn@#Xw*A#]-=A#"
    "a9OA#"
    "d<F&#*;G##.GY##2Sl##6`($#:l:$#>xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4&"
    "#3^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4"
    "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),"
    "#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#"
    "/QHC#3^ZC#7jmC#;v)D#?,<D#C8ND#GDaD#KPsD#O]/"
    "E#g1A5#KA*1#gC17#MGd;#8(02#L-d3#rWM4#Hga1#,<w0#T.j<#O#'2#CYN1#qa^:#_4m3#o@"
    "/=#eG8=#t8J5#`+78#4uI-#"
    "m3B2#SB[8#Q0@8#i[*9#iOn8#1Nm;#^sN9#qh<9#:=x-#P;K2#$%X9#bC+.#Rg;<#mN=.#MTF."
    "#RZO.#2?)4#Y#(/#[)1/#b;L/#dAU/#0Sv;#lY$0#n`-0#sf60#(F24#wrH0#%/e0#"
    "TmD<#%JSMFove:CTBEXI:<eh2g)B,3h2^G3i;#d3jD>)4kMYD4lVu`4m`:&5niUA5@(A5BA1]"
    "PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP"
    "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&"
    "5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp"
    "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#"
    "lLYI:xvD=#";

static const char* GetDefaultCompressedFontDataTTFBase85()
{
    return proggy_clean_ttf_compressed_data_base85;
}
